diff --git a/LogicImplement/README.md b/LogicImplement/README.md deleted file mode 100644 index ea7b369..0000000 --- a/LogicImplement/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Here is the code - diff --git a/LogicImplement/data_process/augmentation_handle.py b/LogicImplement/data_process/augmentation_handle.py deleted file mode 100644 index 7508b54..0000000 --- a/LogicImplement/data_process/augmentation_handle.py +++ /dev/null @@ -1,129 +0,0 @@ -# -*- coding: utf-8 -*- - -import random - -import cv2 -import numpy as np -import os.path -import copy - - -# 椒盐噪声 -def SaltAndPepper(src, percetage=0.2): - SP_NoiseImg = src.copy() - SP_NoiseNum = int(percetage * src.shape[0] * src.shape[1]) - for i in range(SP_NoiseNum): - randR = np.random.randint(0, src.shape[0] - 1) - randG = np.random.randint(0, src.shape[1] - 1) - randB = np.random.randint(0, 3) - if np.random.randint(0, 1) == 0: - SP_NoiseImg[randR, randG, randB] = 0 - else: - SP_NoiseImg[randR, randG, randB] = 255 - return SP_NoiseImg - - -# 高斯噪声 -def addGaussianNoise(image, percetage=0.2): - G_Noiseimg = image.copy() - w = image.shape[1] - h = image.shape[0] - G_NoiseNum = int(percetage * image.shape[0] * image.shape[1]) - for i in range(G_NoiseNum): - temp_x = np.random.randint(0, h) - temp_y = np.random.randint(0, w) - G_Noiseimg[temp_x][temp_y][np.random.randint(3)] = np.random.randn(1)[0] - return G_Noiseimg - - -# 昏暗 -def darker(image, percetage=0.9): - image_copy = image.copy() - w = image.shape[1] - h = image.shape[0] - # get darker - for xi in range(0, w): - for xj in range(0, h): - image_copy[xj, xi, 0] = int(image[xj, xi, 0] * percetage) - image_copy[xj, xi, 1] = int(image[xj, xi, 1] * percetage) - image_copy[xj, xi, 2] = int(image[xj, xi, 2] * percetage) - return image_copy - - -# 亮度 -def brighter(image, percetage=1.5): - image_copy = image.copy() - w = image.shape[1] - h = image.shape[0] - # get brighter - for xi in range(0, w): - for xj in range(0, h): - image_copy[xj, xi, 0] = np.clip(int(image[xj, xi, 0] * percetage), a_max=255, a_min=0) - image_copy[xj, xi, 1] = np.clip(int(image[xj, xi, 1] * percetage), a_max=255, a_min=0) - image_copy[xj, xi, 2] = np.clip(int(image[xj, xi, 2] * percetage), a_max=255, a_min=0) - return image_copy - - -# 旋转 -def rotate(image, angle, center=None, scale=1.0): - (h, w) = image.shape[:2] - # If no rotation center is specified, the center of the image is set as the rotation center - if center is None: - center = (w / 2, h / 2) - m = cv2.getRotationMatrix2D(center, angle, scale) - rotated = cv2.warpAffine(image, m, (w, h)) - return rotated - - -# 翻转 -def flip(image): - flipped_image = np.fliplr(image) - return flipped_image - - -# 图片文件夹路径 -image_dir = r'C:\data\TianChiCTSeg\data\train\image\\' -mask_dir = r'C:\data\TianChiCTSeg\data\train\mask\\' -i = 0 -j = 0 -for img_name in os.listdir(image_dir): - num = random.randint(0, 10) - # print("num:", num) - i += 1 - if num == 2 or num == 3: - j += 1 - print("aug") - img_path = image_dir + img_name - mask_path = mask_dir + img_name - print(img_path) - img = cv2.imread(img_path) - mask = cv2.imread(mask_path) - - rotated_90_image = rotate(img, 90) - rotated_90_mask = rotate(mask, 90) - cv2.imwrite(image_dir + img_name[0:-4] + '_r90.png', rotated_90_image) - cv2.imwrite(mask_dir.replace("image", "mask") + img_name[0:-4] + '_r90.png', rotated_90_mask) - - brighter_image = brighter(img) - # brighter_mask = brighter(mask) - cv2.imwrite(image_dir + img_name[0:-4] + '_brighter.png', brighter_image) - cv2.imwrite(mask_dir.replace("image", "mask") + img_name[0:-4] + '_brighter.png', mask) - - darker_image = darker(img) - # darker_mask = darker(mask) - cv2.imwrite(image_dir + img_name[0:-4] + '_darker.png', darker_image) - cv2.imwrite(mask_dir.replace("image", "mask") + img_name[0:-4] + '_darker.png', mask) - - addGaussianNoise_image = addGaussianNoise(img, 90) - addGaussianNoise_mask = addGaussianNoise(mask, 90) - cv2.imwrite(image_dir + img_name[0:-4] + '_addGaussianNoise.png', addGaussianNoise_image) - cv2.imwrite(mask_dir.replace("image", "mask") + img_name[0:-4] + '_addGaussianNoise.png', addGaussianNoise_mask) - # - SaltAndPepper_image = SaltAndPepper(img, 90) - SaltAndPepper_mask = SaltAndPepper(mask, 90) - cv2.imwrite(image_dir + img_name[0:-4] + '_SaltAndPepper.png', SaltAndPepper_image) - cv2.imwrite(mask_dir.replace("image", "mask") + img_name[0:-4] + '_SaltAndPepper.png', SaltAndPepper_mask) - else: - print("不aug") - print(f"进行到{i}张") - print(f"已增强{j}张") diff --git a/LogicImplement/data_process/data_split.py b/LogicImplement/data_process/data_split.py deleted file mode 100644 index 98ef3f8..0000000 --- a/LogicImplement/data_process/data_split.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- -import os -import random - -import numpy as np -from scipy import io -import cv2 -import matplotlib.pyplot as plt -import h5py -import glob -import pydicom - - -def to255(img): # 归一化到0-255代码 - min_val = img.min() - max_val = img.max() - img = (img - min_val) / (max_val - min_val + 1e-5) # 图像归一化 - img = img * 255 # *255 - return img - -src_path = r'C:\data\TianChiCTSeg\GroundTruth' # mat文件的上级文件夹路径,英文路径 -save_path = r'C:\data\TianChiCTSeg\data' # 保存png路径 - -train_image_path = save_path + '\\' + 'train\\' + 'image' -test_image_path = save_path + '\\' + 'val\\' + 'image' -train_mask_path = save_path + '\\' + 'train\\' + 'mask' -test_mask_path = save_path + '\\' + 'val\\' + 'mask' - -os.makedirs(train_image_path, exist_ok=True) -os.makedirs(test_image_path, exist_ok=True) -os.makedirs(train_mask_path, exist_ok=True) -os.makedirs(test_mask_path, exist_ok=True) - -names = glob.glob(src_path + '\\' + '*.mat') -random.shuffle(names) -order = 0 -for name in names: - # feature=h5py.File(name) #读取mat文件 - feature = io.loadmat(name) # (512, 512, 183), ['__header__', '__version__', '__globals__', 'Mask'] - # print(feature.keys()) - # 获取病人的名字 - this_pat_name = name.split('\\')[-1].split('.')[0] - # 寻找病人对应的image的路径 - pat_image_root_path = r'C:\data\TianChiCTSeg\CT_scans 01' + '\\' + this_pat_name - # 首先获取mask - this_3D_mask = feature['Mask'] - # 下面进行到255的转换 - this_3D_mask[np.where(this_3D_mask == 1)] = 255 - # 下面进行slice的切分 - x, y, z = this_3D_mask.shape - if order % 5 == 0: - # 验证集 - for slice_index in range(z): - this_2D_mask = this_3D_mask[:, :, slice_index] - # 进行保存 - this_save_name = this_pat_name + '_' + 'D' + str(slice_index+1).zfill(4) + '.png' - cv2.imwrite(test_mask_path + '\\' + this_save_name, this_2D_mask) - # 读取原始dcm图像 - ds = pydicom.dcmread(pat_image_root_path + '\\' + 'D' + str(slice_index+1).zfill(4) + '.dcm') - data = ds.pixel_array # 0-4095, 肺部窗宽 1 300 Hu~1 700 Hu,窗位-600 Hu~-800 Hu, - ct_image = to255(data) - cv2.imwrite(test_image_path + '\\' + this_save_name, ct_image) - else: - # 训练集 - for slice_index in range(z): - this_2D_mask = this_3D_mask[:, :, slice_index] - # 进行保存 - this_save_name = this_pat_name + '_' + 'D' + str(slice_index+1).zfill(4) + '.png' - cv2.imwrite(train_mask_path + '\\' + this_save_name, this_2D_mask) - # 读取原始dcm图像 - ds = pydicom.dcmread(pat_image_root_path + '\\' + 'D' + str(slice_index+1).zfill(4) + '.dcm') - data = ds.pixel_array # 0-4095, 肺部窗宽 1 300 Hu~1 700 Hu,窗位-600 Hu~-800 Hu, - - ct_image = to255(data) - cv2.imwrite(train_image_path + '\\' + this_save_name, ct_image) - print(order) - order += 1 - - - - - - diff --git a/LogicImplement/datasets/__init__.py b/LogicImplement/datasets/__init__.py deleted file mode 100644 index 67f768c..0000000 --- a/LogicImplement/datasets/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* diff --git a/LogicImplement/datasets/__pycache__/__init__.cpython-38.pyc b/LogicImplement/datasets/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 4555732..0000000 Binary files a/LogicImplement/datasets/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/datasets/__pycache__/lung.cpython-38.pyc b/LogicImplement/datasets/__pycache__/lung.cpython-38.pyc deleted file mode 100644 index a86d3bf..0000000 Binary files a/LogicImplement/datasets/__pycache__/lung.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/datasets/lung.py b/LogicImplement/datasets/lung.py deleted file mode 100644 index 48bf9ae..0000000 --- a/LogicImplement/datasets/lung.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -from torch.utils.data import Dataset -import torch -from PIL import Image -from utils.tools import process_binary_mask_tensor -# from datasets.make_dataset import make_dataset -import os -import numpy as np - - -class Lung(Dataset): - - CHANNELS_NUM = 3 - NUM_CLASSES = 2 - - MEAN = [0.70791537, 0.59156666, 0.54687498] - STD = [0.15324752, 0.16178547, 0.17681521] - - def __init__(self, mode, transform=None, target_transform=None, BASE_PATH=""): - print(mode) - self.items_image, self.items_mask = make_dataset(mode, BASE_PATH) - self.transform = transform - self.target_transform = target_transform - - def __len__(self): - return len(self.items_image) - - def __str__(self): - return 'Stroke' - - def __getitem__(self, index): - # image_path = self.items[index]['image_path'] - # mask_path = self.items[index]['mask_path'] - image_path = self.items_image[index] - mask_path = self.items_mask[index] - - image = Image.open(image_path).convert('RGB') - mask = Image.open(mask_path).convert('L') - - if self.transform: - image = self.transform(image) - if self.target_transform: - mask = self.target_transform(mask) - - mask = process_binary_mask_tensor(mask) - # print(torch.unique(mask)) - - return image, mask - - -def make_dataset(mode, base_path): - print(mode) - - # assert mode in ['train', 'val'] - # - # path = os.path.join(base_path, mode) - image_path = os.path.join(base_path, "image") - mask_path = os.path.join(base_path, "mask") - # print(image_path) - image_list = [] - for file in os.listdir(image_path): - image_list.append(os.path.join(image_path, file)) - - mask_list = [] - for file in os.listdir(mask_path): - mask_list.append(os.path.join(mask_path, file)) - - # print(image_list) - return image_list, mask_list - diff --git a/LogicImplement/datasets/make_dataset.py b/LogicImplement/datasets/make_dataset.py deleted file mode 100644 index 5f1b66f..0000000 --- a/LogicImplement/datasets/make_dataset.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -import os - - -def make_dataset(mode, base_path, is_contain_augmented_data): - """从指定目录下读取用于training、validation的json文件""" - - assert mode in ['train', 'val'] - - path = os.path.join(base_path, mode) - image_path = os.path.join(path, "image") - mask_path = os.path.join(path, "mask") - - image_list = os.listdir(image_path) - mask_list = os.listdir(mask_path) - return image_list, mask_list \ No newline at end of file diff --git a/LogicImplement/generate_metric.py b/LogicImplement/generate_metric.py deleted file mode 100644 index a957d3f..0000000 --- a/LogicImplement/generate_metric.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- coding: utf-8 -*- -#!/usr/bin/python3 -# -*- coding: utf-8 -* -from random import random - -import torch -from torch.autograd import Variable -from models import unet -from torchvision import transforms -import os -from PIL import Image -import argparse -import numpy as np -from collections import OrderedDict -import cv2 -from utils.metrics import compute_metrics - -# 选择网络模型 -# net = xnet.XNet(num_classes=2, in_channels=3) -net = unet.UNet(num_classes=2, in_channels=3) -# net = unet.UNet(num_classes=2, in_channels=3, is_attention=True) - -# 加载模型 -ckpt = torch.load(r".pkl") -ckpt = ckpt['model_state_dict'] -new_state_dict = OrderedDict() -for k, v in ckpt.items(): - # name = k[7:] # remove `module.`,表面从第7个key值字符取到最后一个字符,正好去掉了module. - new_state_dict[k] = v # 新字典的key值对应的value为一一对应的值。 - -net.load_state_dict(new_state_dict) -net.eval() - -pre_data_path = r"...image" -dst_path = r"...preds" -os.makedirs(dst_path, exist_ok=True) -image_list = [] -for file in os.listdir(pre_data_path): - image_list.append(os.path.join(pre_data_path, file)) - -# 验证 -loss_all = [] -predictions_all = [] -labels_all = [] - - -with torch.no_grad(): - i = 0 - for image in image_list: - print(i) - i += 1 - name = image.split("\\")[-1] - # print(name) - # 读取原图 - org_image = cv2.imread(image, 0) - org_mask = cv2.imread(image.replace('image', 'label'), 0) - - # mask = Image.open(image.replace('image', 'label')).convert('L') - labels = np.zeros(org_mask.shape, np.uint8) - labels[org_mask != 0] = 1 - - image = Image.open(image).convert('RGB') - ori_size = image.size - # image = image.resize((224, 224), resample=NEAREST) - # image = img_to_tensor(image) - image = transforms.ToTensor()(image) - # print(image.shape) # [3, 224, 224] - image = Variable(torch.unsqueeze(image, dim=0).float(), requires_grad=False) - # print(image.shape) # [1, 3, 224, 224] - outputs = net(image) # # [1, 2, 224, 224], 此2应该为类别数 - - if isinstance(outputs, list): - # 若使用deep supervision,用最后一个输出来进行预测 - predictions = torch.max(outputs[-1], dim=1)[1].cpu().numpy().astype(np.int) - else: - # 将概率最大的类别作为预测的类别 - predictions = torch.max(outputs, dim=1)[1].cpu().numpy().astype(np.int) - labels = labels.astype(np.int) - - predictions_all.append(predictions) - labels_all.append(labels) - # softmax = torch.nn.Softmax(dim=1) - # outputs = softmax(outputs) - outputs = outputs.squeeze(0) # [2, 224, 224] - - # mask = np.argmax(outputs, axis=0) - mask = torch.max(outputs, 0)[1].cpu().numpy() - # print(mask.max()) - # print(mask.shape) - a = np.zeros(mask.shape, np.float32) - a[mask == 1] = 255 - # a[mask == 0] = 0 - # for i in range(np.shape(a)[0]): - im = Image.fromarray(np.uint8(a[:, :])) - # 下面进行拼接展示 - - cat_img = np.hstack([org_image, org_mask, a]) - print(dst_path + '\\' + str(name), cat_img) - ##cv2.imwrite(dst_path + '\\' + str(name), cat_img) - - - # 使用混淆矩阵计算语义分割中的指标 - iou, miou, dsc, mdsc, ac, pc, mpc, se, mse, sp, msp, f1, mf1 = compute_metrics(predictions_all, labels_all, - num_classes=2) - print('Training: MIoU: {:.4f}, MDSC: {:.4f}, MPC: {:.4f}, AC: {:.4f}, MSE: {:.4f}, MSP: {:.4f}, MF1: {:.4f}'.format( - miou, mdsc, mpc, ac, mse, msp, mf1 - )) - # im = im.resize(ori_size, resample=NEAREST) # 需要使用最近邻插值 - # im.convert('RGB').save(dst_path + "/" + str(name)) - - diff --git a/LogicImplement/generate_predict.py b/LogicImplement/generate_predict.py deleted file mode 100644 index 812a17f..0000000 --- a/LogicImplement/generate_predict.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- -#!/usr/bin/python3 -# -*- coding: utf-8 -* -from random import random - -import torch -import time - -import torchvision -from PIL.Image import NEAREST -from albumentations.pytorch.functional import img_to_tensor -from torch.autograd import Variable -from torch.utils.data import DataLoader -from torch.utils.tensorboard import SummaryWriter -from models import unet, nested_unet, loss_function, xnet, nfn_plus, unet_tiny -from datasets import lung -from torchvision import transforms -import os -from PIL import Image -import argparse -import numpy as np -from collections import OrderedDict -import cv2 - -# 选择网络模型 - -strat_time = time.time() -# net = unet.UNet(num_classes=2, in_channels=3) -save_name = "" -net = unet.UNet(num_classes=2, in_channels=3) -# 加载模型 -ckpt = torch.load(r".pkl") -ckpt = ckpt['model_state_dict'] -# new_state_dict = OrderedDict() -# for k, v in ckpt.items(): -# name = k[7:] # remove `module.`,表面从第7个key值字符取到最后一个字符,正好去掉了module. -# new_state_dict[name] = v # 新字典的key值对应的value为一一对应的值。 - -net.load_state_dict(ckpt) -net.eval() - -pre_data_path = r".../val/image" -dst_path = r"..." + "/" + save_name -os.makedirs(dst_path, exist_ok=True) - -image_list = [] -for file in os.listdir(pre_data_path): - image_list.append(os.path.join(pre_data_path, file)) - -with torch.no_grad(): - i = 0 - for image in image_list: - print(i) - i += 1 - name = image.split("/")[-1] - gt_mask = cv2.imread(image.replace('image', 'mask'), 0) - if len(np.unique(gt_mask)) > 1: - image = Image.open(image).convert('RGB') - ori_size = image.size - - image = transforms.ToTensor()(image) - # image = Variable(torch.unsqueeze(image, dim=0).float(), requires_grad=False) - image = torch.unsqueeze(image, dim=0) - outputs = net(image) - outputs = outputs.squeeze(0) # [2, 224, 224] - - if outputs.shape[0] == 2: - mask = torch.max(outputs, 0)[1].cpu().numpy() - a = np.zeros(mask.shape, np.uint8) - a[mask == 1] = 255 - cat = np.hstack([gt_mask, a]) - print(dst_path + "/" + str(name), cat) - #cv2.imwrite(dst_path + "/" + str(name), cat) - else: - pass - - -end_time = time.time() -seg_time = end_time - strat_time -print(save_name + "总耗时为:" + str(seg_time)) - - diff --git a/LogicImplement/models/U2net.py b/LogicImplement/models/U2net.py deleted file mode 100644 index 6440202..0000000 --- a/LogicImplement/models/U2net.py +++ /dev/null @@ -1,523 +0,0 @@ -# -*- coding: utf-8 -*- - -import torch -from torch import nn - -import torch.nn.functional as F - - -class REBNCONV(nn.Module): - def __init__(self, in_ch=3, out_ch=3, dirate=1): - super(REBNCONV, self).__init__() - - self.conv_s1 = nn.Conv2d(in_ch, out_ch, 3, padding=1 * dirate, dilation=1 * dirate) - self.bn_s1 = nn.BatchNorm2d(out_ch) - self.relu_s1 = nn.ReLU(inplace=True) - - def forward(self, x): - hx = x - xout = self.relu_s1(self.bn_s1(self.conv_s1(hx))) - - return xout - - -## upsample tensor 'src' to have the same spatial size with tensor 'tar' -def _upsample_like(src, tar): - src = F.upsample(src, size=tar.shape[2:], mode='bilinear') - - return src - - -### RSU-7 ### -class RSU7(nn.Module): # UNet07DRES(nn.Module): - - def __init__(self, in_ch=3, mid_ch=12, out_ch=3): - super(RSU7, self).__init__() - - self.rebnconvin = REBNCONV(in_ch, out_ch, dirate=1) - - self.rebnconv1 = REBNCONV(out_ch, mid_ch, dirate=1) - self.pool1 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv2 = REBNCONV(mid_ch, mid_ch, dirate=1) - self.pool2 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv3 = REBNCONV(mid_ch, mid_ch, dirate=1) - self.pool3 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv4 = REBNCONV(mid_ch, mid_ch, dirate=1) - self.pool4 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv5 = REBNCONV(mid_ch, mid_ch, dirate=1) - self.pool5 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv6 = REBNCONV(mid_ch, mid_ch, dirate=1) - - self.rebnconv7 = REBNCONV(mid_ch, mid_ch, dirate=2) - - self.rebnconv6d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv5d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv4d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv3d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv2d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv1d = REBNCONV(mid_ch * 2, out_ch, dirate=1) - - def forward(self, x): - hx = x - hxin = self.rebnconvin(hx) - - hx1 = self.rebnconv1(hxin) - hx = self.pool1(hx1) - - hx2 = self.rebnconv2(hx) - hx = self.pool2(hx2) - - hx3 = self.rebnconv3(hx) - hx = self.pool3(hx3) - - hx4 = self.rebnconv4(hx) - hx = self.pool4(hx4) - - hx5 = self.rebnconv5(hx) - hx = self.pool5(hx5) - - hx6 = self.rebnconv6(hx) - - hx7 = self.rebnconv7(hx6) - - hx6d = self.rebnconv6d(torch.cat((hx7, hx6), 1)) - hx6dup = _upsample_like(hx6d, hx5) - - hx5d = self.rebnconv5d(torch.cat((hx6dup, hx5), 1)) - hx5dup = _upsample_like(hx5d, hx4) - - hx4d = self.rebnconv4d(torch.cat((hx5dup, hx4), 1)) - hx4dup = _upsample_like(hx4d, hx3) - - hx3d = self.rebnconv3d(torch.cat((hx4dup, hx3), 1)) - hx3dup = _upsample_like(hx3d, hx2) - - hx2d = self.rebnconv2d(torch.cat((hx3dup, hx2), 1)) - hx2dup = _upsample_like(hx2d, hx1) - - hx1d = self.rebnconv1d(torch.cat((hx2dup, hx1), 1)) - - return hx1d + hxin - - -### RSU-6 ### -class RSU6(nn.Module): # UNet06DRES(nn.Module): - - def __init__(self, in_ch=3, mid_ch=12, out_ch=3): - super(RSU6, self).__init__() - - self.rebnconvin = REBNCONV(in_ch, out_ch, dirate=1) - - self.rebnconv1 = REBNCONV(out_ch, mid_ch, dirate=1) - self.pool1 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv2 = REBNCONV(mid_ch, mid_ch, dirate=1) - self.pool2 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv3 = REBNCONV(mid_ch, mid_ch, dirate=1) - self.pool3 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv4 = REBNCONV(mid_ch, mid_ch, dirate=1) - self.pool4 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv5 = REBNCONV(mid_ch, mid_ch, dirate=1) - - self.rebnconv6 = REBNCONV(mid_ch, mid_ch, dirate=2) - - self.rebnconv5d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv4d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv3d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv2d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv1d = REBNCONV(mid_ch * 2, out_ch, dirate=1) - - def forward(self, x): - hx = x - - hxin = self.rebnconvin(hx) - - hx1 = self.rebnconv1(hxin) - hx = self.pool1(hx1) - - hx2 = self.rebnconv2(hx) - hx = self.pool2(hx2) - - hx3 = self.rebnconv3(hx) - hx = self.pool3(hx3) - - hx4 = self.rebnconv4(hx) - hx = self.pool4(hx4) - - hx5 = self.rebnconv5(hx) - - hx6 = self.rebnconv6(hx5) - - hx5d = self.rebnconv5d(torch.cat((hx6, hx5), 1)) - hx5dup = _upsample_like(hx5d, hx4) - - hx4d = self.rebnconv4d(torch.cat((hx5dup, hx4), 1)) - hx4dup = _upsample_like(hx4d, hx3) - - hx3d = self.rebnconv3d(torch.cat((hx4dup, hx3), 1)) - hx3dup = _upsample_like(hx3d, hx2) - - hx2d = self.rebnconv2d(torch.cat((hx3dup, hx2), 1)) - hx2dup = _upsample_like(hx2d, hx1) - - hx1d = self.rebnconv1d(torch.cat((hx2dup, hx1), 1)) - - return hx1d + hxin - - -### RSU-5 ### -class RSU5(nn.Module): # UNet05DRES(nn.Module): - - def __init__(self, in_ch=3, mid_ch=12, out_ch=3): - super(RSU5, self).__init__() - - self.rebnconvin = REBNCONV(in_ch, out_ch, dirate=1) - - self.rebnconv1 = REBNCONV(out_ch, mid_ch, dirate=1) - self.pool1 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv2 = REBNCONV(mid_ch, mid_ch, dirate=1) - self.pool2 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv3 = REBNCONV(mid_ch, mid_ch, dirate=1) - self.pool3 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv4 = REBNCONV(mid_ch, mid_ch, dirate=1) - - self.rebnconv5 = REBNCONV(mid_ch, mid_ch, dirate=2) - - self.rebnconv4d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv3d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv2d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv1d = REBNCONV(mid_ch * 2, out_ch, dirate=1) - - def forward(self, x): - hx = x - - hxin = self.rebnconvin(hx) - - hx1 = self.rebnconv1(hxin) - hx = self.pool1(hx1) - - hx2 = self.rebnconv2(hx) - hx = self.pool2(hx2) - - hx3 = self.rebnconv3(hx) - hx = self.pool3(hx3) - - hx4 = self.rebnconv4(hx) - - hx5 = self.rebnconv5(hx4) - - hx4d = self.rebnconv4d(torch.cat((hx5, hx4), 1)) - hx4dup = _upsample_like(hx4d, hx3) - - hx3d = self.rebnconv3d(torch.cat((hx4dup, hx3), 1)) - hx3dup = _upsample_like(hx3d, hx2) - - hx2d = self.rebnconv2d(torch.cat((hx3dup, hx2), 1)) - hx2dup = _upsample_like(hx2d, hx1) - - hx1d = self.rebnconv1d(torch.cat((hx2dup, hx1), 1)) - - return hx1d + hxin - - -### RSU-4 ### -class RSU4(nn.Module): # UNet04DRES(nn.Module): - - def __init__(self, in_ch=3, mid_ch=12, out_ch=3): - super(RSU4, self).__init__() - - self.rebnconvin = REBNCONV(in_ch, out_ch, dirate=1) - - self.rebnconv1 = REBNCONV(out_ch, mid_ch, dirate=1) - self.pool1 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv2 = REBNCONV(mid_ch, mid_ch, dirate=1) - self.pool2 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.rebnconv3 = REBNCONV(mid_ch, mid_ch, dirate=1) - - self.rebnconv4 = REBNCONV(mid_ch, mid_ch, dirate=2) - - self.rebnconv3d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv2d = REBNCONV(mid_ch * 2, mid_ch, dirate=1) - self.rebnconv1d = REBNCONV(mid_ch * 2, out_ch, dirate=1) - - def forward(self, x): - hx = x - - hxin = self.rebnconvin(hx) - - hx1 = self.rebnconv1(hxin) - hx = self.pool1(hx1) - - hx2 = self.rebnconv2(hx) - hx = self.pool2(hx2) - - hx3 = self.rebnconv3(hx) - - hx4 = self.rebnconv4(hx3) - - hx3d = self.rebnconv3d(torch.cat((hx4, hx3), 1)) - hx3dup = _upsample_like(hx3d, hx2) - - hx2d = self.rebnconv2d(torch.cat((hx3dup, hx2), 1)) - hx2dup = _upsample_like(hx2d, hx1) - - hx1d = self.rebnconv1d(torch.cat((hx2dup, hx1), 1)) - - return hx1d + hxin - - -### RSU-4F ### -class RSU4F(nn.Module): # UNet04FRES(nn.Module): - - def __init__(self, in_ch=3, mid_ch=12, out_ch=3): - super(RSU4F, self).__init__() - - self.rebnconvin = REBNCONV(in_ch, out_ch, dirate=1) - - self.rebnconv1 = REBNCONV(out_ch, mid_ch, dirate=1) - self.rebnconv2 = REBNCONV(mid_ch, mid_ch, dirate=2) - self.rebnconv3 = REBNCONV(mid_ch, mid_ch, dirate=4) - - self.rebnconv4 = REBNCONV(mid_ch, mid_ch, dirate=8) - - self.rebnconv3d = REBNCONV(mid_ch * 2, mid_ch, dirate=4) - self.rebnconv2d = REBNCONV(mid_ch * 2, mid_ch, dirate=2) - self.rebnconv1d = REBNCONV(mid_ch * 2, out_ch, dirate=1) - - def forward(self, x): - hx = x - - hxin = self.rebnconvin(hx) - - hx1 = self.rebnconv1(hxin) - hx2 = self.rebnconv2(hx1) - hx3 = self.rebnconv3(hx2) - - hx4 = self.rebnconv4(hx3) - - hx3d = self.rebnconv3d(torch.cat((hx4, hx3), 1)) - hx2d = self.rebnconv2d(torch.cat((hx3d, hx2), 1)) - hx1d = self.rebnconv1d(torch.cat((hx2d, hx1), 1)) - - return hx1d + hxin - - -##### U^2-Net #### -class U2NET(nn.Module): - - def __init__(self, in_ch=3, out_ch=1): - super(U2NET, self).__init__() - - self.stage1 = RSU7(in_ch, 32, 64) - self.pool12 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.stage2 = RSU6(64, 32, 128) - self.pool23 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.stage3 = RSU5(128, 64, 256) - self.pool34 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.stage4 = RSU4(256, 128, 512) - self.pool45 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.stage5 = RSU4F(512, 256, 512) - self.pool56 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.stage6 = RSU4F(512, 256, 512) - - # decoder - self.stage5d = RSU4F(1024, 256, 512) - self.stage4d = RSU4(1024, 128, 256) - self.stage3d = RSU5(512, 64, 128) - self.stage2d = RSU6(256, 32, 64) - self.stage1d = RSU7(128, 16, 64) - - self.side1 = nn.Conv2d(64, out_ch, 3, padding=1) - self.side2 = nn.Conv2d(64, out_ch, 3, padding=1) - self.side3 = nn.Conv2d(128, out_ch, 3, padding=1) - self.side4 = nn.Conv2d(256, out_ch, 3, padding=1) - self.side5 = nn.Conv2d(512, out_ch, 3, padding=1) - self.side6 = nn.Conv2d(512, out_ch, 3, padding=1) - - self.outconv = nn.Conv2d(6 * out_ch, out_ch, 1) - - def forward(self, x): - hx = x - - # stage 1 - hx1 = self.stage1(hx) - hx = self.pool12(hx1) - - # stage 2 - hx2 = self.stage2(hx) - hx = self.pool23(hx2) - - # stage 3 - hx3 = self.stage3(hx) - hx = self.pool34(hx3) - - # stage 4 - hx4 = self.stage4(hx) - hx = self.pool45(hx4) - - # stage 5 - hx5 = self.stage5(hx) - hx = self.pool56(hx5) - - # stage 6 - hx6 = self.stage6(hx) - hx6up = _upsample_like(hx6, hx5) - - # -------------------- decoder -------------------- - hx5d = self.stage5d(torch.cat((hx6up, hx5), 1)) - hx5dup = _upsample_like(hx5d, hx4) - - hx4d = self.stage4d(torch.cat((hx5dup, hx4), 1)) - hx4dup = _upsample_like(hx4d, hx3) - - hx3d = self.stage3d(torch.cat((hx4dup, hx3), 1)) - hx3dup = _upsample_like(hx3d, hx2) - - hx2d = self.stage2d(torch.cat((hx3dup, hx2), 1)) - hx2dup = _upsample_like(hx2d, hx1) - - hx1d = self.stage1d(torch.cat((hx2dup, hx1), 1)) - - # side output - d1 = self.side1(hx1d) - - d2 = self.side2(hx2d) - d2 = _upsample_like(d2, d1) - - d3 = self.side3(hx3d) - d3 = _upsample_like(d3, d1) - - d4 = self.side4(hx4d) - d4 = _upsample_like(d4, d1) - - d5 = self.side5(hx5d) - d5 = _upsample_like(d5, d1) - - d6 = self.side6(hx6) - d6 = _upsample_like(d6, d1) - - d0 = self.outconv(torch.cat((d1, d2, d3, d4, d5, d6), 1)) - - return F.sigmoid(d0), F.sigmoid(d1), F.sigmoid(d2), F.sigmoid(d3), F.sigmoid(d4), F.sigmoid(d5), F.sigmoid(d6) - - -### U^2-Net small ### -class U2NETP(nn.Module): - - def __init__(self, in_ch=3, out_ch=1): - super(U2NETP, self).__init__() - - self.stage1 = RSU7(in_ch, 16, 64) - self.pool12 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.stage2 = RSU6(64, 16, 64) - self.pool23 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.stage3 = RSU5(64, 16, 64) - self.pool34 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.stage4 = RSU4(64, 16, 64) - self.pool45 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.stage5 = RSU4F(64, 16, 64) - self.pool56 = nn.MaxPool2d(2, stride=2, ceil_mode=True) - - self.stage6 = RSU4F(64, 16, 64) - - # decoder - self.stage5d = RSU4F(128, 16, 64) - self.stage4d = RSU4(128, 16, 64) - self.stage3d = RSU5(128, 16, 64) - self.stage2d = RSU6(128, 16, 64) - self.stage1d = RSU7(128, 16, 64) - - self.side1 = nn.Conv2d(64, out_ch, 3, padding=1) - self.side2 = nn.Conv2d(64, out_ch, 3, padding=1) - self.side3 = nn.Conv2d(64, out_ch, 3, padding=1) - self.side4 = nn.Conv2d(64, out_ch, 3, padding=1) - self.side5 = nn.Conv2d(64, out_ch, 3, padding=1) - self.side6 = nn.Conv2d(64, out_ch, 3, padding=1) - - self.outconv = nn.Conv2d(6 * out_ch, out_ch, 1) - - def forward(self, x): - hx = x # [B, c_in, 128, 128] - - # stage 1 - hx1 = self.stage1(hx) # [8, 64, 128, 128] - hx = self.pool12(hx1) # [8, 64, 64, 64] - - # stage 2 - hx2 = self.stage2(hx) - hx = self.pool23(hx2) - - # stage 3 - hx3 = self.stage3(hx) - hx = self.pool34(hx3) - - # stage 4 - hx4 = self.stage4(hx) - hx = self.pool45(hx4) - - # stage 5 - hx5 = self.stage5(hx) - hx = self.pool56(hx5) - - # stage 6 - hx6 = self.stage6(hx) # [8, 64, 4, 4] - hx6up = _upsample_like(hx6, hx5) - - # decoder - hx5d = self.stage5d(torch.cat((hx6up, hx5), 1)) - hx5dup = _upsample_like(hx5d, hx4) - - hx4d = self.stage4d(torch.cat((hx5dup, hx4), 1)) - hx4dup = _upsample_like(hx4d, hx3) - - hx3d = self.stage3d(torch.cat((hx4dup, hx3), 1)) - hx3dup = _upsample_like(hx3d, hx2) - - hx2d = self.stage2d(torch.cat((hx3dup, hx2), 1)) - hx2dup = _upsample_like(hx2d, hx1) - - hx1d = self.stage1d(torch.cat((hx2dup, hx1), 1)) - - # side output - d1 = self.side1(hx1d) - - d2 = self.side2(hx2d) - d2 = _upsample_like(d2, d1) - - d3 = self.side3(hx3d) - d3 = _upsample_like(d3, d1) - - d4 = self.side4(hx4d) - d4 = _upsample_like(d4, d1) - - d5 = self.side5(hx5d) - d5 = _upsample_like(d5, d1) - - d6 = self.side6(hx6) - d6 = _upsample_like(d6, d1) - - d0 = self.outconv(torch.cat((d1, d2, d3, d4, d5, d6), 1)) - - return F.sigmoid(d0), F.sigmoid(d1), F.sigmoid(d2), F.sigmoid(d3), F.sigmoid(d4), F.sigmoid(d5), F.sigmoid(d6) diff --git a/LogicImplement/models/__init__.py b/LogicImplement/models/__init__.py deleted file mode 100644 index 67f768c..0000000 --- a/LogicImplement/models/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* diff --git a/LogicImplement/models/__pycache__/U2net.cpython-38.pyc b/LogicImplement/models/__pycache__/U2net.cpython-38.pyc deleted file mode 100644 index ec17f6d..0000000 Binary files a/LogicImplement/models/__pycache__/U2net.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/models/__pycache__/__init__.cpython-38.pyc b/LogicImplement/models/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 85b9541..0000000 Binary files a/LogicImplement/models/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/models/__pycache__/loss_function.cpython-38.pyc b/LogicImplement/models/__pycache__/loss_function.cpython-38.pyc deleted file mode 100644 index 15e7a61..0000000 Binary files a/LogicImplement/models/__pycache__/loss_function.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/models/__pycache__/lovasz_losses.cpython-38.pyc b/LogicImplement/models/__pycache__/lovasz_losses.cpython-38.pyc deleted file mode 100644 index 939fb98..0000000 Binary files a/LogicImplement/models/__pycache__/lovasz_losses.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/models/__pycache__/nested_unet.cpython-38.pyc b/LogicImplement/models/__pycache__/nested_unet.cpython-38.pyc deleted file mode 100644 index 3db6671..0000000 Binary files a/LogicImplement/models/__pycache__/nested_unet.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/models/__pycache__/network_blocks.cpython-38.pyc b/LogicImplement/models/__pycache__/network_blocks.cpython-38.pyc deleted file mode 100644 index 2eb7be6..0000000 Binary files a/LogicImplement/models/__pycache__/network_blocks.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/models/__pycache__/nfn_plus.cpython-38.pyc b/LogicImplement/models/__pycache__/nfn_plus.cpython-38.pyc deleted file mode 100644 index 9f71eef..0000000 Binary files a/LogicImplement/models/__pycache__/nfn_plus.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/models/__pycache__/unet.cpython-38.pyc b/LogicImplement/models/__pycache__/unet.cpython-38.pyc deleted file mode 100644 index c946e5b..0000000 Binary files a/LogicImplement/models/__pycache__/unet.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/models/__pycache__/unet_tiny.cpython-38.pyc b/LogicImplement/models/__pycache__/unet_tiny.cpython-38.pyc deleted file mode 100644 index c7ff844..0000000 Binary files a/LogicImplement/models/__pycache__/unet_tiny.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/models/__pycache__/xnet.cpython-38.pyc b/LogicImplement/models/__pycache__/xnet.cpython-38.pyc deleted file mode 100644 index 4fc06fe..0000000 Binary files a/LogicImplement/models/__pycache__/xnet.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/models/compute_flops.py b/LogicImplement/models/compute_flops.py deleted file mode 100644 index 2655459..0000000 --- a/LogicImplement/models/compute_flops.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- - - -import torchvision.models as models -import torch -from ptflops import get_model_complexity_info -from models import unet, nested_unet, loss_function, xnet, nfn_plus, lovasz_losses, unet_tiny, U2net - - -with torch.cuda.device(0): - net = U2net.U2NET(out_ch=2, in_ch=3) - # net = unet.UNet(num_classes=2, in_channels=3, is_attention=True, - # is_recurrent_residual=False) -# net = models.densenet161() - macs, params = get_model_complexity_info(net, (3, 128, 128), as_strings=True, - print_per_layer_stat=True, verbose=True) - print('{:<30} {:<8}'.format('Computational complexity: ', macs)) - print('{:<30} {:<8}'.format('Number of parameters: ', params)) - -""" -U2NETP: Number of parameters: 1.13 M -U2NET: Number of parameters: 44.02 M -""" \ No newline at end of file diff --git a/LogicImplement/models/loss_function.py b/LogicImplement/models/loss_function.py deleted file mode 100644 index 618ebaf..0000000 --- a/LogicImplement/models/loss_function.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -from models.lovasz_losses import lovasz_softmax -from torch import nn -from torch.nn import functional as F -import torch - - -class LovaszLoss(nn.Module): - """lovasz损失函数,https://github.com/bermanmaxim/LovaszSoftmax""" - - def __init__(self): - super().__init__() - - def forward(self, input, target): - probability = F.softmax(input, dim=1) - loss = lovasz_softmax(probability, target) - return loss - - -class SoftDiceLoss(nn.Module): - """Dice损失函数,仅用于二分类,https://www.aiuai.cn/aifarm1159.html,https://github.com/pytorch/pytorch/issues/1249""" - - def __init__(self, num_classes, smooth=1): - """:arg smooth Dice系数分母分子上添加的参数,防止除0错误""" - - super().__init__() - self.num_classes = num_classes - self.smooth = smooth - - def forward(self, input, target): - # 确保SoftDiceLoss只用于二分类问题 - assert torch.max(target).item() <= 1, 'SoftDiceLoss()目前只能用于二分类' - - batch_size = input.size(0) - probability = F.softmax(input, dim=1) - - # 将probability和target矩阵flatten为B*(C*H*W)的矩阵 - probability = probability.view(batch_size, -1) - # 这里的target label需要是one hot格式,one hot要显示传入类别数量 - target_one_hot = F.one_hot(target, num_classes=self.num_classes).permute((0, 3, 1, 2)) - target_one_hot = target_one_hot.contiguous().view(batch_size, -1) - # 计算相交区域的概率矩阵 - intersection = probability * target_one_hot - - # 计算每一个batch的Dice相似系数(Dice similar coefficient) - dsc = (2 * intersection.sum(dim=1) + self.smooth) / ( - target_one_hot.sum(dim=1) + probability.sum(dim=1) + self.smooth) - loss = (1 - dsc).sum() - - return loss - - -# class BCELoss2d(nn.Module): -# """二进制交叉熵,计算概率的函数可选Sigmoid和Softmax,https://www.aiuai.cn/aifarm1159.html""" -# -# def __init__(self, p_function='sigmoid', weight=None, size_average=True): -# super().__init__() -# -# assert p_function in ['softmax', 'sigmoid'] -# -# self.p_function = p_function -# self.bce_loss_function = nn.BCELoss(weight=weight, size_average=size_average) -# -# def forward(self, input, target): -# if self.p_function == 'softmax': -# probability = F.softmax(input, dim=1) -# else: -# probability = F.sigmoid(input) -# loss = self.bce_loss_function(probability, target) -# return loss - -class DiceAndBCELoss(nn.Module): - """Dice损失和BCE损失的混合""" - - def __init__(self, num_classes): - super().__init__() - self.dice_loss = SoftDiceLoss(num_classes=num_classes) - self.bce_loss = nn.CrossEntropyLoss() - - def forward(self, input, target): - return self.dice_loss(input, target) + self.bce_loss(input, target) - - -class SCELoss(nn.Module): - """用于Learning with noisy labels的对称交叉熵损失函数""" - - def __init__(self, num_classes, alpha=1.0, beta=1.0): - super().__init__() - - self.alpha = alpha - self.beta = beta - self.num_classes = num_classes - self.cross_entropy = nn.CrossEntropyLoss() - - def forward(self, input, target): - # CCE - ce = self.cross_entropy(input, target) - - # RCE - input = F.softmax(input, dim=1) - input = torch.clamp(input, min=1e-7, max=1.0) - target_one_hot = F.one_hot(target, self.num_classes).permute((0, 3, 1, 2)).float() - target_one_hot = torch.clamp(target_one_hot, min=1e-4, max=1.0) - rce = (-1 * torch.sum(input * torch.log(target_one_hot), dim=1)) - - # Loss - loss = self.alpha * ce + self.beta * rce.mean() - return loss - - def __str__(self): - return 'SCELoss(alpha={}, beta={})'.format(self.alpha, self.beta) diff --git a/LogicImplement/models/lovasz_losses.py b/LogicImplement/models/lovasz_losses.py deleted file mode 100644 index 57f61c3..0000000 --- a/LogicImplement/models/lovasz_losses.py +++ /dev/null @@ -1,254 +0,0 @@ -""" -Lovasz-Softmax and Jaccard hinge loss in PyTorch -Maxim Berman 2018 ESAT-PSI KU Leuven (MIT License) -""" - -from __future__ import print_function, division - -import torch -from torch.autograd import Variable -import torch.nn.functional as F -import numpy as np -try: - from itertools import ifilterfalse -except ImportError: - from itertools import filterfalse as ifilterfalse - - -def lovasz_grad(gt_sorted): - """ - Computes gradient of the Lovasz extension w.r.t sorted errors - See Alg. 1 in paper - """ - p = len(gt_sorted) - gts = gt_sorted.sum() - intersection = gts - gt_sorted.float().cumsum(0) - union = gts + (1 - gt_sorted).float().cumsum(0) - jaccard = 1. - intersection / union - if p > 1: # cover 1-pixel case - jaccard[1:p] = jaccard[1:p] - jaccard[0:-1] - return jaccard - - -def iou_binary(preds, labels, EMPTY=1., ignore=None, per_image=True): - """ - IoU for foreground class - binary: 1 foreground, 0 background - """ - if not per_image: - preds, labels = (preds,), (labels,) - ious = [] - for pred, label in zip(preds, labels): - intersection = ((label == 1) & (pred == 1)).sum() - union = ((label == 1) | ((pred == 1) & (label != ignore))).sum() - if not union: - iou = EMPTY - else: - iou = float(intersection) / float(union) - ious.append(iou) - iou = mean(ious) # mean accross images if per_image - return 100 * iou - - -def iou(preds, labels, C, EMPTY=1., ignore=None, per_image=False): - """ - Array of IoU for each (non ignored) class - """ - if not per_image: - preds, labels = (preds,), (labels,) - ious = [] - for pred, label in zip(preds, labels): - iou = [] - for i in range(C): - if i != ignore: # The ignored label is sometimes among predicted classes (ENet - CityScapes) - intersection = ((label == i) & (pred == i)).sum() - union = ((label == i) | ((pred == i) & (label != ignore))).sum() - if not union: - iou.append(EMPTY) - else: - iou.append(float(intersection) / float(union)) - ious.append(iou) - ious = [mean(iou) for iou in zip(*ious)] # mean accross images if per_image - return 100 * np.array(ious) - - -# --------------------------- BINARY LOSSES --------------------------- - - -def lovasz_hinge(logits, labels, per_image=True, ignore=None): - """ - Binary Lovasz hinge loss - logits: [B, H, W] Variable, logits at each pixel (between -\infty and +\infty) - labels: [B, H, W] Tensor, binary ground truth masks (0 or 1) - per_image: compute the loss per image instead of per batch - ignore: void class id - """ - if per_image: - loss = mean(lovasz_hinge_flat(*flatten_binary_scores(log.unsqueeze(0), lab.unsqueeze(0), ignore)) - for log, lab in zip(logits, labels)) - else: - loss = lovasz_hinge_flat(*flatten_binary_scores(logits, labels, ignore)) - return loss - - -def lovasz_hinge_flat(logits, labels): - """ - Binary Lovasz hinge loss - logits: [P] Variable, logits at each prediction (between -\infty and +\infty) - labels: [P] Tensor, binary ground truth labels (0 or 1) - ignore: label to ignore - """ - if len(labels) == 0: - # only void pixels, the gradients should be 0 - return logits.sum() * 0. - signs = 2. * labels.float() - 1. - errors = (1. - logits * Variable(signs)) - errors_sorted, perm = torch.sort(errors, dim=0, descending=True) - perm = perm.data - gt_sorted = labels[perm] - grad = lovasz_grad(gt_sorted) - loss = torch.dot(F.relu(errors_sorted), Variable(grad)) - return loss - - -def flatten_binary_scores(scores, labels, ignore=None): - """ - Flattens predictions in the batch (binary case) - Remove labels equal to 'ignore' - """ - scores = scores.view(-1) - labels = labels.view(-1) - if ignore is None: - return scores, labels - valid = (labels != ignore) - vscores = scores[valid] - vlabels = labels[valid] - return vscores, vlabels - - -class StableBCELoss(torch.nn.modules.Module): - def __init__(self): - super(StableBCELoss, self).__init__() - def forward(self, input, target): - neg_abs = - input.abs() - loss = input.clamp(min=0) - input * target + (1 + neg_abs.exp()).log() - return loss.mean() - - -def binary_xloss(logits, labels, ignore=None): - """ - Binary Cross entropy loss - logits: [B, H, W] Variable, logits at each pixel (between -\infty and +\infty) - labels: [B, H, W] Tensor, binary ground truth masks (0 or 1) - ignore: void class id - """ - logits, labels = flatten_binary_scores(logits, labels, ignore) - loss = StableBCELoss()(logits, Variable(labels.float())) - return loss - - -# --------------------------- MULTICLASS LOSSES --------------------------- - - -def lovasz_softmax(probas, labels, classes='present', per_image=False, ignore=None): - """ - Multi-class Lovasz-Softmax loss - probas: [B, C, H, W] Variable, class probabilities at each prediction (between 0 and 1). - Interpreted as binary (sigmoid) output with outputs of size [B, H, W]. - labels: [B, H, W] Tensor, ground truth labels (between 0 and C - 1) - classes: 'all' for all, 'present' for classes present in labels, or a list of classes to average. - per_image: compute the loss per image instead of per batch - ignore: void class labels - """ - if per_image: - loss = mean(lovasz_softmax_flat(*flatten_probas(prob.unsqueeze(0), lab.unsqueeze(0), ignore), classes=classes) - for prob, lab in zip(probas, labels)) - else: - loss = lovasz_softmax_flat(*flatten_probas(probas, labels, ignore), classes=classes) - return loss - - -def lovasz_softmax_flat(probas, labels, classes='present'): - """ - Multi-class Lovasz-Softmax loss - probas: [P, C] Variable, class probabilities at each prediction (between 0 and 1) - labels: [P] Tensor, ground truth labels (between 0 and C - 1) - classes: 'all' for all, 'present' for classes present in labels, or a list of classes to average. - """ - if probas.numel() == 0: - # only void pixels, the gradients should be 0 - return probas * 0. - C = probas.size(1) - losses = [] - class_to_sum = list(range(C)) if classes in ['all', 'present'] else classes - for c in class_to_sum: - fg = (labels == c).float() # foreground for class c - if (classes == 'present' and fg.sum() == 0): - continue - if C == 1: - if len(classes) > 1: - raise ValueError('Sigmoid output possible only with 1 class') - class_pred = probas[:, 0] - else: - class_pred = probas[:, c] - errors = (Variable(fg) - class_pred).abs() - errors_sorted, perm = torch.sort(errors, 0, descending=True) - perm = perm.data - fg_sorted = fg[perm] - loss = torch.dot(errors_sorted, Variable(lovasz_grad(fg_sorted))) - # loss = loss * (c + 1) - if c == 2: - loss = loss * 2 - losses.append(loss) # 3cls:0.6294, 0.5980 - return mean(losses) - - -def flatten_probas(probas, labels, ignore=None): - """ - Flattens predictions in the batch - """ - if probas.dim() == 3: - # assumes output of a sigmoid layer - B, H, W = probas.size() - probas = probas.view(B, 1, H, W) - B, C, H, W = probas.size() - probas = probas.permute(0, 2, 3, 1).contiguous().view(-1, C) # B * H * W, C = P, C - labels = labels.view(-1) - if ignore is None: - return probas, labels - valid = (labels != ignore) - vprobas = probas[valid.nonzero().squeeze()] - vlabels = labels[valid] - return vprobas, vlabels - -def xloss(logits, labels, ignore=None): - """ - Cross entropy loss - """ - return F.cross_entropy(logits, Variable(labels), ignore_index=255) - - -# --------------------------- HELPER FUNCTIONS --------------------------- -def isnan(x): - return x != x - - -def mean(l, ignore_nan=False, empty=0): - """ - nanmean compatible with generators. - """ - l = iter(l) - if ignore_nan: - l = ifilterfalse(isnan, l) - try: - n = 1 - acc = next(l) - except StopIteration: - if empty == 'raise': - raise ValueError('Empty mean') - return empty - for n, v in enumerate(l, 2): - acc += v - if n == 1: - return acc - return acc / n diff --git a/LogicImplement/models/nested_unet.py b/LogicImplement/models/nested_unet.py deleted file mode 100644 index f8fd947..0000000 --- a/LogicImplement/models/nested_unet.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -from torch import nn -import torch -from .network_blocks import DoubleConvBlock - - -class NestedUNet(nn.Module): - - def __init__(self, num_classes, in_channels, is_deep_supervision=True, is_dense_connections=True, l_num=4): - super().__init__() - - assert l_num in [2, 3, 4], "目前实现的UNet++只有4层" - - nb_filters = [32, 64, 128, 256, 512] - - self.is_deep_supervision = is_deep_supervision - self.is_dense_connections = is_dense_connections - self.l_num = l_num - - # 这2个层内没有需要学习的参数,可以共享 - # 使用MaxPooling进行2倍下采样 - self.downsampling = nn.MaxPool2d(kernel_size=2, stride=2) - # 使用双线性插值进行2倍上采样,对于pixel-wise任务,align_corners设置为True - self.upsampling = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True) - - self.conv0_0 = DoubleConvBlock(in_channels, nb_filters[0], nb_filters[0]) - self.conv1_0 = DoubleConvBlock(nb_filters[0], nb_filters[1], nb_filters[1]) - self.conv2_0 = DoubleConvBlock(nb_filters[1], nb_filters[2], nb_filters[2]) - self.conv3_0 = DoubleConvBlock(nb_filters[2], nb_filters[3], nb_filters[3]) - self.conv4_0 = DoubleConvBlock(nb_filters[3], nb_filters[4], nb_filters[4]) - - self.conv0_1 = DoubleConvBlock(nb_filters[0] + nb_filters[1], nb_filters[0], nb_filters[0]) - self.conv1_1 = DoubleConvBlock(nb_filters[1] + nb_filters[2], nb_filters[1], nb_filters[1]) - self.conv2_1 = DoubleConvBlock(nb_filters[2] + nb_filters[3], nb_filters[2], nb_filters[2]) - self.conv3_1 = DoubleConvBlock(nb_filters[3] + nb_filters[4], nb_filters[3], nb_filters[3]) - - # 是否使用dense connections - if self.is_dense_connections: - self.conv0_2 = DoubleConvBlock(nb_filters[0] * 2 + nb_filters[1], nb_filters[0], nb_filters[0]) - self.conv1_2 = DoubleConvBlock(nb_filters[1] * 2 + nb_filters[2], nb_filters[1], nb_filters[1]) - self.conv2_2 = DoubleConvBlock(nb_filters[2] * 2 + nb_filters[3], nb_filters[2], nb_filters[2]) - - self.conv0_3 = DoubleConvBlock(nb_filters[0] * 3 + nb_filters[1], nb_filters[0], nb_filters[0]) - self.conv1_3 = DoubleConvBlock(nb_filters[1] * 3 + nb_filters[2], nb_filters[1], nb_filters[1]) - - self.conv0_4 = DoubleConvBlock(nb_filters[0] * 4 + nb_filters[1], nb_filters[0], nb_filters[0]) - else: - self.conv0_2 = DoubleConvBlock(nb_filters[0] + nb_filters[1], nb_filters[0], nb_filters[0]) - self.conv1_2 = DoubleConvBlock(nb_filters[1] + nb_filters[2], nb_filters[1], nb_filters[1]) - self.conv2_2 = DoubleConvBlock(nb_filters[2] + nb_filters[3], nb_filters[2], nb_filters[2]) - - self.conv0_3 = DoubleConvBlock(nb_filters[0] + nb_filters[1], nb_filters[0], nb_filters[0]) - self.conv1_3 = DoubleConvBlock(nb_filters[1] + nb_filters[2], nb_filters[1], nb_filters[1]) - - self.conv0_4 = DoubleConvBlock(nb_filters[0] + nb_filters[1], nb_filters[0], nb_filters[0]) - - # 如果使用deep supervision,则每层UNet都有一个输出 - if self.is_deep_supervision: - self.final1 = nn.Conv2d(nb_filters[0], num_classes, kernel_size=1) - self.final2 = nn.Conv2d(nb_filters[0], num_classes, kernel_size=1) - self.final3 = nn.Conv2d(nb_filters[0], num_classes, kernel_size=1) - self.final4 = nn.Conv2d(nb_filters[0], num_classes, kernel_size=1) - else: - self.final = nn.Conv2d(nb_filters[0], num_classes, kernel_size=1) - - # 删除多余层的变量 - if self.l_num == 2: - del self.conv3_0, self.conv2_1, self.conv1_2, self.conv0_3 - del self.conv4_0, self.conv3_1, self.conv2_2, self.conv1_3, self.conv0_4 - if self.l_num == 3: - del self.conv4_0, self.conv3_1, self.conv2_2, self.conv1_3, self.conv0_4 - - def forward(self, input): - x0_0 = self.conv0_0(input) - x1_0 = self.conv1_0(self.downsampling(x0_0)) - x0_1 = self.conv0_1(torch.cat([x0_0, self.upsampling(x1_0)], dim=1)) - - x2_0 = self.conv2_0(self.downsampling(x1_0)) - x1_1 = self.conv1_1(torch.cat([x1_0, self.upsampling(x2_0)], dim=1)) - x0_2 = self.conv0_2(torch.cat([x0_0, x0_1, self.upsampling(x1_1)], dim=1)) - - if self.l_num == 2: - if self.is_deep_supervision: - return [self.final1(x0_1), self.final2(x0_2)] - else: - return self.final(x0_2) - - x3_0 = self.conv3_0(self.downsampling(x2_0)) - x2_1 = self.conv2_1(torch.cat([x2_0, self.upsampling(x3_0)], dim=1)) - x1_2 = self.conv1_2(torch.cat([x1_0, x1_1, self.upsampling(x2_1)], dim=1)) - x0_3 = self.conv0_3(torch.cat([x0_0, x0_1, x0_2, self.upsampling(x1_2)], dim=1)) - - if self.l_num == 3: - if self.is_deep_supervision: - return [self.final1(x0_1), self.final2(x0_2), self.final3(x0_3)] - else: - return self.final(x0_3) - - x4_0 = self.conv4_0(self.downsampling(x3_0)) - x3_1 = self.conv3_1(torch.cat([x3_0, self.upsampling(x4_0)], dim=1)) - x2_2 = self.conv2_2(torch.cat([x2_0, x2_1, self.upsampling(x3_1)], dim=1)) - x1_3 = self.conv1_3(torch.cat([x1_0, x1_1, x1_2, self.upsampling(x2_2)], dim=1)) - x0_4 = self.conv0_4(torch.cat([x0_0, x0_1, x0_2, x0_3, self.upsampling(x1_3)], dim=1)) - - if self.is_deep_supervision: - return [self.final1(x0_1), self.final2(x0_2), self.final3(x0_3), self.final4(x0_4)] - else: - return self.final(x0_4) - - def __str__(self): - return 'UNet++(is_deep_supervision={}, is_dense_connections={}, l_num={})'.format( - self.is_deep_supervision, self.is_dense_connections, self.l_num) diff --git a/LogicImplement/models/network_blocks.py b/LogicImplement/models/network_blocks.py deleted file mode 100644 index b7c6b8d..0000000 --- a/LogicImplement/models/network_blocks.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -# 所用到的所有网络的基本模块 -from torch import nn -from torch.nn import functional as F - - -class SingleConvBlock(nn.Module): - """Conv-BN-ReLU的基本模块,其中的Conv并不会张量的尺寸""" - - def __init__(self, in_channels, out_channels): - super().__init__() - self.block = nn.Sequential( - nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1), - nn.BatchNorm2d(out_channels), - nn.ReLU(inplace=True) - ) - - def forward(self, input): - return self.block(input) - - -class DoubleConvBlock(nn.Module): - """由2个Conv-BN-ReLu组成的基本模块""" - - def __init__(self, in_channels, middle_channels, out_channels, dropout=False, p=0.2): - super().__init__() - # layers = [ - # SingleConvBlock(in_channels, middle_channels), - # SingleConvBlock(middle_channels, out_channels) - # ] - # - # if dropout: - # layers.append(nn.Dropout()) - - # 将Dropout加在2个卷积层的中间 - layers = [SingleConvBlock(in_channels, middle_channels)] - if dropout: - layers.append(nn.Dropout(p=p)) - layers.append(SingleConvBlock(middle_channels, out_channels)) - - self.block = nn.Sequential(*layers) - - def forward(self, input): - return self.block(input) - - -class RecurrentSingleBlock(nn.Module): - """具有循环结构的SingleBlock,其in_channels=out_channels""" - - def __init__(self, channels, t): - assert t > 1, "循环次数必须>1" - super().__init__() - self.t = t - self.block = SingleConvBlock(channels, channels) - - def forward(self, input): - for i in range(self.t): - if i == 0: - output = self.block(input) - # 循环时,要加上最开始的输入 - output = self.block(output + input) - return output - - -class ResidualRecurrentBlock(nn.Module): - """由2个RecurrentBlock和残差连接组成的模块""" - - def __init__(self, in_channels, out_channels, t=2): - super().__init__() - self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0) - self.r2_block = nn.Sequential( - # 在循环模块前加一个Conv用于改变数据的channel数 - RecurrentSingleBlock(out_channels, t), - RecurrentSingleBlock(out_channels, t) - ) - - def forward(self, input): - # 残差连接 - conv_input = self.conv(input) - return self.r2_block(conv_input) + conv_input - - -class AttentionBlock2d(nn.Module): - """注意力模块,计算较粗糙、低语义的feature map中每个点的注意力系数,并返回乘以注意力系数的feature map, - 按照论文作者的代码进行实现的 - https://github.com/ozan-oktay/Attention-Gated-Networks/blob/master/models/layers/grid_attention_layer.py""" - - def __init__(self, x_channels, gate_channels, inter_channels=None, subsample_factor=(2, 2)): - super().__init__() - assert isinstance(subsample_factor, tuple), "subsample_factor必须是tuple类型" - - if inter_channels is None: - inter_channels = x_channels // 2 - if inter_channels == 0: - inter_channels = 1 - - # TODO: 这里的下采样系数subsample_factor有什么用,bias为什么为False - self.x_conv = nn.Conv2d(x_channels, inter_channels, kernel_size=subsample_factor, stride=subsample_factor, - padding=0, bias=False) - self.gate_conv = nn.Conv2d(gate_channels, inter_channels, kernel_size=1, stride=1, padding=0) - self.psi = nn.Conv2d(inter_channels, 1, kernel_size=1, stride=1, padding=0) - - # 将最终乘以注意力系数的feature map再进行一次卷积 - # TODO: 为什么后面还要接一个BN - self.end_transform = nn.Sequential( - nn.Conv2d(x_channels, x_channels, kernel_size=1, stride=1, padding=0), - nn.BatchNorm2d(x_channels) - ) - - # TODO: 是否需要初始化参数 - - def forward(self, x, gate): - origin_x = x - origin_x_size = x.size() - - x = self.x_conv(x) - gate = self.gate_conv(gate) - # 将卷积后的gate上采样为x的尺寸 - gate = F.upsample_bilinear(gate, size=x.size()[2:]) - - # 将feature map相加后进行ReLU - temp = F.relu(gate + x, inplace=True) - temp = self.psi(temp) - attention_coefficient = F.sigmoid(temp) - - # 将注意力系数上采样为原始x的尺寸,并扩展为对应的channel数 - attention_coefficient = F.upsample_bilinear(attention_coefficient, size=origin_x_size[2:]) - attention_coefficient = attention_coefficient.expand_as(origin_x) - - # 注意力系数乘原始的x - output = attention_coefficient * origin_x - output = self.end_transform(output) - - return output diff --git a/LogicImplement/models/nfn_plus.py b/LogicImplement/models/nfn_plus.py deleted file mode 100644 index 3c783c9..0000000 --- a/LogicImplement/models/nfn_plus.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -import torch -from torch import nn -from .network_blocks import DoubleConvBlock - - -class OutputBlock(nn.Module): - """论文中提到的输出块""" - - def __init__(self, in_channels, out_channels): - super().__init__() - self.block = nn.Sequential( - nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1), - nn.BatchNorm2d(out_channels), - nn.ReLU(inplace=True), - ) - - def forward(self, input): - return self.block(input) - - -class NFNPlus(nn.Module): - """具有2个Stage的UNet结构的网络,添加了UNet间的skip connection, - https://www.sciencedirect.com/science/article/pii/S0893608020300721 - """ - - def __init__(self, num_classes, in_channels, is_deep_supervision=True, p=0.2): - super().__init__() - nb_filters = [32, 64, 128] - self.is_deep_supervision = is_deep_supervision - - # 标准的2倍上采样和下采样,因为没有可以学习的参数,可以共享 - self.down = nn.MaxPool2d(kernel_size=2, stride=2) - self.up = nn.UpsamplingBilinear2d(scale_factor=2) - - # Front Network - self.conv0_0 = DoubleConvBlock(in_channels, nb_filters[0], nb_filters[0], dropout=True, p=p) - self.conv0_1 = DoubleConvBlock(nb_filters[0], nb_filters[1], nb_filters[1], dropout=True, p=p) - self.conv0_2 = DoubleConvBlock(nb_filters[1], nb_filters[2], nb_filters[2], dropout=True, p=p) - self.conv0_3 = DoubleConvBlock(nb_filters[2] + nb_filters[1], nb_filters[1], nb_filters[1], dropout=True, p=p) - self.conv0_4 = DoubleConvBlock(nb_filters[1] + nb_filters[0], nb_filters[0], nb_filters[0], dropout=True, p=p) - - # Followed Network - self.conv1_0 = DoubleConvBlock(nb_filters[0], nb_filters[0], nb_filters[0], dropout=True, p=p) - self.conv1_1 = DoubleConvBlock(nb_filters[0] + nb_filters[1], nb_filters[1], nb_filters[1], dropout=True, p=p) - self.conv1_2 = DoubleConvBlock(nb_filters[1] + nb_filters[2], nb_filters[2], nb_filters[2], dropout=True, p=p) - self.conv1_3 = DoubleConvBlock(nb_filters[2] + nb_filters[1], nb_filters[1], nb_filters[1], dropout=True, p=p) - self.conv1_4 = DoubleConvBlock(nb_filters[1] + nb_filters[0], nb_filters[0], nb_filters[0], dropout=True, p=p) - - if is_deep_supervision: - self.final0 = OutputBlock(nb_filters[0], num_classes) - self.final1 = OutputBlock(nb_filters[0], num_classes) - else: - self.final = OutputBlock(nb_filters[0], num_classes) - - def forward(self, input): - # Front Network - x0_0 = self.conv0_0(input) - x0_1 = self.conv0_1(self.down(x0_0)) - x0_2 = self.conv0_2(self.down(x0_1)) - x0_3 = self.conv0_3(torch.cat([x0_1, self.up(x0_2)], dim=1)) - x0_4 = self.conv0_4(torch.cat([x0_0, self.up(x0_3)], dim=1)) - - # Followed Network - x1_0 = self.conv1_0(x0_4) - x1_1 = self.conv1_1(torch.cat([x0_3, self.down(x1_0)], dim=1)) - x1_2 = self.conv1_2(torch.cat([x0_2, self.down(x1_1)], dim=1)) - x1_3 = self.conv1_3(torch.cat([x1_1, self.up(x1_2)], dim=1)) - x1_4 = self.conv1_4(torch.cat([x1_0, self.up(x1_3)], dim=1)) - - if self.is_deep_supervision: - return [self.final0(x0_4), self.final1(x1_4)] - else: - return self.final(x1_4) - - def __str__(self): - return 'NFN+' diff --git a/LogicImplement/models/unet.py b/LogicImplement/models/unet.py deleted file mode 100644 index 0aa76de..0000000 --- a/LogicImplement/models/unet.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -import torch -from torch import nn -from .network_blocks import DoubleConvBlock, AttentionBlock2d, ResidualRecurrentBlock - - -class UNet(nn.Module): - """基本的UNet网络,其中的卷积并不会改变张量的尺寸""" - - def __init__(self, num_classes, in_channels, is_attention=False, is_recurrent_residual=False): - super().__init__() - nb_filters = [32, 64, 128, 256, 512] - self.is_attention = is_attention - self.is_recurrent_residual = is_recurrent_residual - - # 标准的2倍上采样和下采样,因为没有可以学习的参数,可以共享 - self.down = nn.MaxPool2d(kernel_size=2, stride=2) - self.up = nn.UpsamplingBilinear2d(scale_factor=2) - - if is_recurrent_residual: - # 下采样的模块 - self.conv0_0 = ResidualRecurrentBlock(in_channels, nb_filters[0]) - self.conv1_0 = ResidualRecurrentBlock(nb_filters[0], nb_filters[1]) - self.conv2_0 = ResidualRecurrentBlock(nb_filters[1], nb_filters[2]) - self.conv3_0 = ResidualRecurrentBlock(nb_filters[2], nb_filters[3]) - self.conv4_0 = ResidualRecurrentBlock(nb_filters[3], nb_filters[4]) - - # 上采样的模块 - self.conv3_1 = ResidualRecurrentBlock(nb_filters[4] + nb_filters[3], nb_filters[3]) - self.conv2_2 = ResidualRecurrentBlock(nb_filters[3] + nb_filters[2], nb_filters[2]) - self.conv1_3 = ResidualRecurrentBlock(nb_filters[2] + nb_filters[1], nb_filters[1]) - self.conv0_4 = ResidualRecurrentBlock(nb_filters[1] + nb_filters[0], nb_filters[0]) - else: - # 下采样的模块 - self.conv0_0 = DoubleConvBlock(in_channels, nb_filters[0], nb_filters[0]) - self.conv1_0 = DoubleConvBlock(nb_filters[0], nb_filters[1], nb_filters[1]) - self.conv2_0 = DoubleConvBlock(nb_filters[1], nb_filters[2], nb_filters[2]) - self.conv3_0 = DoubleConvBlock(nb_filters[2], nb_filters[3], nb_filters[3]) - self.conv4_0 = DoubleConvBlock(nb_filters[3], nb_filters[4], nb_filters[4]) - - # 上采样的模块 - self.conv3_1 = DoubleConvBlock(nb_filters[4] + nb_filters[3], nb_filters[3], nb_filters[3]) - self.conv2_2 = DoubleConvBlock(nb_filters[3] + nb_filters[2], nb_filters[2], nb_filters[2]) - self.conv1_3 = DoubleConvBlock(nb_filters[2] + nb_filters[1], nb_filters[1], nb_filters[1]) - self.conv0_4 = DoubleConvBlock(nb_filters[1] + nb_filters[0], nb_filters[0], nb_filters[0]) - - # 最后接一个Conv计算在所有类别上的分数 - self.final = nn.Conv2d(nb_filters[0], num_classes, kernel_size=1, stride=1) - - if is_attention: - self.attention3_1 = AttentionBlock2d(nb_filters[3], nb_filters[4]) - self.attention2_2 = AttentionBlock2d(nb_filters[2], nb_filters[3]) - self.attention1_3 = AttentionBlock2d(nb_filters[1], nb_filters[2]) - self.attention0_4 = AttentionBlock2d(nb_filters[0], nb_filters[1]) - - def forward(self, input): - # 下采样编码 - x0_0 = self.conv0_0(input) - x1_0 = self.conv1_0(self.down(x0_0)) - x2_0 = self.conv2_0(self.down(x1_0)) - x3_0 = self.conv3_0(self.down(x2_0)) - x4_0 = self.conv4_0(self.down(x3_0)) - - if self.is_attention: - # 特征融合并进行上采样解码,使用concatenate进行特征融合 - x3_1 = self.conv3_1(torch.cat([self.attention3_1(x3_0, x4_0), self.up(x4_0)], dim=1)) - x2_2 = self.conv2_2(torch.cat([self.attention2_2(x2_0, x3_1), self.up(x3_1)], dim=1)) - x1_3 = self.conv1_3(torch.cat([self.attention1_3(x1_0, x2_2), self.up(x2_2)], dim=1)) - x0_4 = self.conv0_4(torch.cat([self.attention0_4(x0_0, x1_3), self.up(x1_3)], dim=1)) - else: - # 特征融合并进行上采样解码,使用concatenate进行特征融合 - x3_1 = self.conv3_1(torch.cat([x3_0, self.up(x4_0)], dim=1)) - x2_2 = self.conv2_2(torch.cat([x2_0, self.up(x3_1)], dim=1)) - x1_3 = self.conv1_3(torch.cat([x1_0, self.up(x2_2)], dim=1)) - x0_4 = self.conv0_4(torch.cat([x0_0, self.up(x1_3)], dim=1)) - - # 计算每个类别上的得分 - output = self.final(x0_4) - - return output - - def __str__(self): - ret = 'U-Net' - if self.is_recurrent_residual: - ret = 'R2U-Net' - if self.is_attention: - ret = 'Attention ' + ret - return ret diff --git a/LogicImplement/models/unet_resnet34.py b/LogicImplement/models/unet_resnet34.py deleted file mode 100644 index ca13a22..0000000 --- a/LogicImplement/models/unet_resnet34.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -# 基于ResNet34实现的UNet,将ResNet34作为UNet的Encoder模块 -import torch -from torch import nn -from torch.nn import functional as F -from torchvision import models - - -class _DecoderBlock(nn.Module): - """UNet中的解码模块,标准UNet中的Decoder模块""" - - def __init__(self, in_channels, middle_channels, out_channels=None): - super().__init__() - - # 进行2次卷积后,再进行上采样(除了最后的输出的那一层) - layers = [ - nn.Conv2d(in_channels, middle_channels, kernel_size=3), - nn.BatchNorm2d(middle_channels), - nn.ReLU(inplace=True), - nn.Conv2d(middle_channels, middle_channels, kernel_size=3), - nn.BatchNorm2d(middle_channels), - nn.ReLU(inplace=True) - ] - - if out_channels: - # 最后进行2倍上采样 - layers.append(nn.ConvTranspose2d(middle_channels, out_channels, kernel_size=2, stride=2)) - - self.decode = nn.Sequential(*layers) - - def forward(self, input): - return self.decode(input) - - -class UNet_ResNet34(nn.Module): - """使用预训练的ResNet34(整体上分为4个stage,和UNet的下采样过程相似)作为Encoder实现的UNet""" - - def __init__(self, num_classes): - super().__init__() - - # 加载预训练的ResNet34 - resnet34 = models.resnet34(pretrained=True) - - self.enc1 = nn.Sequential( - resnet34.conv1, - resnet34.bn1, - resnet34.relu, - resnet34.maxpool, - # ResNet34中的layer1并不会进行下采样,所以将其和前面的层合并到一起 - resnet34.layer1 - ) - # 使用ResNet34中主要的4个stage最为UNet的Encoder层 - self.enc2 = resnet34.layer2 - self.enc3 = resnet34.layer3 - self.enc4 = resnet34.layer4 - - self.center = _DecoderBlock(512, 1024, 512) - - self.dec4 = _DecoderBlock(1024, 512, 256) - self.dec3 = _DecoderBlock(512, 256, 128) - self.dec2 = _DecoderBlock(256, 128, 64) - # 最后一层不进行上采样与降维 - self.dec1 = _DecoderBlock(128, 64) - - # 获得每个像素,在每个类别上的得分 - self.final = nn.Conv2d(64, num_classes, kernel_size=1) - - def forward(self, input): - enc1 = self.enc1(input) - enc2 = self.enc2(enc1) - enc3 = self.enc3(enc2) - enc4 = self.enc4(enc3) - - center = self.center(enc4) - - # 进行特征融合与上采样 - dec4 = self.dec4(self.feature_fusion(enc4, center)) - dec3 = self.dec3(self.feature_fusion(enc3, dec4)) - dec2 = self.dec2(self.feature_fusion(enc2, dec3)) - dec1 = self.dec1(self.feature_fusion(enc1, dec2)) - - # 计算每个像素在每个类别上的得分 - final = self.final(dec1) - - # 最后通过上采样,将feature map还原到输入图像的尺寸 - return F.upsample_bilinear(final, input.shape[2:]) - - def feature_fusion(self, t1, t2): - """特征融合,将其中的一个张量进行下采样后(使其与另一个张量保持相同的shape),与另一个张量进行拼接""" - - # 假设输入的张量都是正方形的 - if t1.shape[-1] >= t2.shape[-1]: - max_t = t1 - min_t = t2 - else: - max_t = t2 - min_t = t1 - - return torch.cat([F.upsample_bilinear(max_t, min_t.shape[2:]), min_t], dim=1) diff --git a/LogicImplement/models/unet_tiny.py b/LogicImplement/models/unet_tiny.py deleted file mode 100644 index 013ba8a..0000000 --- a/LogicImplement/models/unet_tiny.py +++ /dev/null @@ -1,241 +0,0 @@ -# -*- coding: utf-8 -*- - -#!/usr/bin/python3 -# -*- coding: utf-8 -* -import torch -from torch import nn -from .network_blocks import DoubleConvBlock, AttentionBlock2d, ResidualRecurrentBlock - -class UNet3tiny(nn.Module): - """基本的UNet网络,其中的卷积并不会改变张量的尺寸""" - - def __init__(self, num_classes, in_channels, is_attention=False, is_recurrent_residual=False): - super().__init__() - nb_filters = [32, 64, 128, 256, 512] - self.is_attention = is_attention - self.is_recurrent_residual = is_recurrent_residual - - # 标准的2倍上采样和下采样,因为没有可以学习的参数,可以共享 - self.down = nn.MaxPool2d(kernel_size=2, stride=2) - self.up = nn.UpsamplingBilinear2d(scale_factor=2) - - if is_recurrent_residual: - # 下采样的模块 - self.conv0_0 = ResidualRecurrentBlock(in_channels, nb_filters[0]) - self.conv1_0 = ResidualRecurrentBlock(nb_filters[0], nb_filters[1]) - self.conv2_0 = ResidualRecurrentBlock(nb_filters[1], nb_filters[2]) - self.conv3_0 = ResidualRecurrentBlock(nb_filters[2], nb_filters[3]) - self.conv4_0 = ResidualRecurrentBlock(nb_filters[3], nb_filters[4]) - - # 上采样的模块 - self.conv3_1 = ResidualRecurrentBlock(nb_filters[4] + nb_filters[3], nb_filters[3]) - self.conv2_2 = ResidualRecurrentBlock(nb_filters[3] + nb_filters[2], nb_filters[2]) - self.conv1_3 = ResidualRecurrentBlock(nb_filters[2] + nb_filters[1], nb_filters[1]) - self.conv0_4 = ResidualRecurrentBlock(nb_filters[1] + nb_filters[0], nb_filters[0]) - else: - # 下采样的模块 - self.conv0_0 = DoubleConvBlock(in_channels, nb_filters[0], nb_filters[0]) - self.conv1_0 = DoubleConvBlock(nb_filters[0], nb_filters[1], nb_filters[1]) - self.conv2_0 = DoubleConvBlock(nb_filters[1], nb_filters[2], nb_filters[2]) - self.conv3_0 = DoubleConvBlock(nb_filters[2], nb_filters[3], nb_filters[3]) - - # 上采样的模块 - self.conv2_2 = DoubleConvBlock(nb_filters[3] + nb_filters[2], nb_filters[2], nb_filters[2]) - self.conv1_3 = DoubleConvBlock(nb_filters[2] + nb_filters[1], nb_filters[1], nb_filters[1]) - self.conv0_4 = DoubleConvBlock(nb_filters[1] + nb_filters[0], nb_filters[0], nb_filters[0]) - - # 最后接一个Conv计算在所有类别上的分数 - self.final = nn.Conv2d(nb_filters[0], num_classes, kernel_size=1, stride=1) - - if is_attention: - self.attention3_1 = AttentionBlock2d(nb_filters[3], nb_filters[4]) - self.attention2_2 = AttentionBlock2d(nb_filters[2], nb_filters[3]) - self.attention1_3 = AttentionBlock2d(nb_filters[1], nb_filters[2]) - self.attention0_4 = AttentionBlock2d(nb_filters[0], nb_filters[1]) - - def forward(self, input): - # 下采样编码 - x0_0 = self.conv0_0(input) - x1_0 = self.conv1_0(self.down(x0_0)) - x2_0 = self.conv2_0(self.down(x1_0)) - x3_0 = self.conv3_0(self.down(x2_0)) - # x4_0 = self.conv4_0(self.down(x3_0)) - - if self.is_attention: - # 特征融合并进行上采样解码,使用concatenate进行特征融合 - x3_1 = self.conv3_1(torch.cat([self.attention3_1(x3_0, x4_0), self.up(x4_0)], dim=1)) - x2_2 = self.conv2_2(torch.cat([self.attention2_2(x2_0, x3_1), self.up(x3_1)], dim=1)) - x1_3 = self.conv1_3(torch.cat([self.attention1_3(x1_0, x2_2), self.up(x2_2)], dim=1)) - x0_4 = self.conv0_4(torch.cat([self.attention0_4(x0_0, x1_3), self.up(x1_3)], dim=1)) - else: - # 特征融合并进行上采样解码,使用concatenate进行特征融合 - # x3_1 = self.conv3_1(torch.cat([x3_0, self.up(x4_0)], dim=1)) - x2_2 = self.conv2_2(torch.cat([x2_0, self.up(x3_0)], dim=1)) - x1_3 = self.conv1_3(torch.cat([x1_0, self.up(x2_2)], dim=1)) - x0_4 = self.conv0_4(torch.cat([x0_0, self.up(x1_3)], dim=1)) - - # 计算每个类别上的得分 - output = self.final(x0_4) - - return output - - def __str__(self): - ret = 'U-Net' - if self.is_recurrent_residual: - ret = 'R2U-Net' - if self.is_attention: - ret = 'Attention ' + ret - return ret - -class UNet2tiny(nn.Module): - """基本的UNet网络,其中的卷积并不会改变张量的尺寸""" - - def __init__(self, num_classes, in_channels, is_attention=False, is_recurrent_residual=False): - super().__init__() - nb_filters = [32, 64, 128, 256, 512] - self.is_attention = is_attention - self.is_recurrent_residual = is_recurrent_residual - - # 标准的2倍上采样和下采样,因为没有可以学习的参数,可以共享 - self.down = nn.MaxPool2d(kernel_size=2, stride=2) - self.up = nn.UpsamplingBilinear2d(scale_factor=2) - - if is_recurrent_residual: - # 下采样的模块 - self.conv0_0 = ResidualRecurrentBlock(in_channels, nb_filters[0]) - self.conv1_0 = ResidualRecurrentBlock(nb_filters[0], nb_filters[1]) - self.conv2_0 = ResidualRecurrentBlock(nb_filters[1], nb_filters[2]) - self.conv3_0 = ResidualRecurrentBlock(nb_filters[2], nb_filters[3]) - self.conv4_0 = ResidualRecurrentBlock(nb_filters[3], nb_filters[4]) - - # 上采样的模块 - self.conv3_1 = ResidualRecurrentBlock(nb_filters[4] + nb_filters[3], nb_filters[3]) - self.conv2_2 = ResidualRecurrentBlock(nb_filters[3] + nb_filters[2], nb_filters[2]) - self.conv1_3 = ResidualRecurrentBlock(nb_filters[2] + nb_filters[1], nb_filters[1]) - self.conv0_4 = ResidualRecurrentBlock(nb_filters[1] + nb_filters[0], nb_filters[0]) - else: - # 下采样的模块 - self.conv0_0 = DoubleConvBlock(in_channels, nb_filters[0], nb_filters[0]) - self.conv1_0 = DoubleConvBlock(nb_filters[0], nb_filters[1], nb_filters[1]) - self.conv2_0 = DoubleConvBlock(nb_filters[1], nb_filters[2], nb_filters[2]) - - - # 上采样的模块 - - self.conv1_3 = DoubleConvBlock(nb_filters[2] + nb_filters[1], nb_filters[1], nb_filters[1]) - self.conv0_4 = DoubleConvBlock(nb_filters[1] + nb_filters[0], nb_filters[0], nb_filters[0]) - - # 最后接一个Conv计算在所有类别上的分数 - self.final = nn.Conv2d(nb_filters[0], num_classes, kernel_size=1, stride=1) - - if is_attention: - self.attention3_1 = AttentionBlock2d(nb_filters[3], nb_filters[4]) - self.attention2_2 = AttentionBlock2d(nb_filters[2], nb_filters[3]) - self.attention1_3 = AttentionBlock2d(nb_filters[1], nb_filters[2]) - self.attention0_4 = AttentionBlock2d(nb_filters[0], nb_filters[1]) - - def forward(self, input): - # 下采样编码 - x0_0 = self.conv0_0(input) - x1_0 = self.conv1_0(self.down(x0_0)) - x2_0 = self.conv2_0(self.down(x1_0)) - # x3_0 = self.conv3_0(self.down(x2_0)) - # x4_0 = self.conv4_0(self.down(x3_0)) - - if self.is_attention: - # 特征融合并进行上采样解码,使用concatenate进行特征融合 - x3_1 = self.conv3_1(torch.cat([self.attention3_1(x3_0, x4_0), self.up(x4_0)], dim=1)) - x2_2 = self.conv2_2(torch.cat([self.attention2_2(x2_0, x3_1), self.up(x3_1)], dim=1)) - x1_3 = self.conv1_3(torch.cat([self.attention1_3(x1_0, x2_2), self.up(x2_2)], dim=1)) - x0_4 = self.conv0_4(torch.cat([self.attention0_4(x0_0, x1_3), self.up(x1_3)], dim=1)) - else: - # 特征融合并进行上采样解码,使用concatenate进行特征融合 - # x3_1 = self.conv3_1(torch.cat([x3_0, self.up(x4_0)], dim=1)) - # x2_2 = self.conv2_2(torch.cat([x2_0, self.up(x3_1)], dim=1)) - x1_3 = self.conv1_3(torch.cat([x1_0, self.up(x2_0)], dim=1)) - x0_4 = self.conv0_4(torch.cat([x0_0, self.up(x1_3)], dim=1)) - - # 计算每个类别上的得分 - output = self.final(x0_4) - - return output - - def __str__(self): - ret = 'U-Net' - if self.is_recurrent_residual: - ret = 'R2U-Net' - if self.is_attention: - ret = 'Attention ' + ret - return ret - -class UNet1tiny(nn.Module): - """基本的UNet网络,其中的卷积并不会改变张量的尺寸""" - - def __init__(self, num_classes, in_channels, is_attention=False, is_recurrent_residual=False): - super().__init__() - nb_filters = [32, 64, 128, 256, 512] - self.is_attention = is_attention - self.is_recurrent_residual = is_recurrent_residual - - # 标准的2倍上采样和下采样,因为没有可以学习的参数,可以共享 - self.down = nn.MaxPool2d(kernel_size=2, stride=2) - self.up = nn.UpsamplingBilinear2d(scale_factor=2) - - if is_recurrent_residual: - # 下采样的模块 - self.conv0_0 = ResidualRecurrentBlock(in_channels, nb_filters[0]) - self.conv1_0 = ResidualRecurrentBlock(nb_filters[0], nb_filters[1]) - self.conv2_0 = ResidualRecurrentBlock(nb_filters[1], nb_filters[2]) - self.conv3_0 = ResidualRecurrentBlock(nb_filters[2], nb_filters[3]) - self.conv4_0 = ResidualRecurrentBlock(nb_filters[3], nb_filters[4]) - - # 上采样的模块 - self.conv3_1 = ResidualRecurrentBlock(nb_filters[4] + nb_filters[3], nb_filters[3]) - self.conv2_2 = ResidualRecurrentBlock(nb_filters[3] + nb_filters[2], nb_filters[2]) - self.conv1_3 = ResidualRecurrentBlock(nb_filters[2] + nb_filters[1], nb_filters[1]) - self.conv0_4 = ResidualRecurrentBlock(nb_filters[1] + nb_filters[0], nb_filters[0]) - else: - # 下采样的模块 - self.conv0_0 = DoubleConvBlock(in_channels, nb_filters[0], nb_filters[0]) - self.conv1_0 = DoubleConvBlock(nb_filters[0], nb_filters[1], nb_filters[1]) - - - # 上采样的模块 - - self.conv0_4 = DoubleConvBlock(nb_filters[1] + nb_filters[0], nb_filters[0], nb_filters[0]) - - # 最后接一个Conv计算在所有类别上的分数 - self.final = nn.Conv2d(nb_filters[0], num_classes, kernel_size=1, stride=1) - - if is_attention: - self.attention3_1 = AttentionBlock2d(nb_filters[3], nb_filters[4]) - self.attention2_2 = AttentionBlock2d(nb_filters[2], nb_filters[3]) - self.attention1_3 = AttentionBlock2d(nb_filters[1], nb_filters[2]) - self.attention0_4 = AttentionBlock2d(nb_filters[0], nb_filters[1]) - - def forward(self, input): - # 下采样编码 - x0_0 = self.conv0_0(input) - x1_0 = self.conv1_0(self.down(x0_0)) - - if self.is_attention: - # 特征融合并进行上采样解码,使用concatenate进行特征融合 - x3_1 = self.conv3_1(torch.cat([self.attention3_1(x3_0, x4_0), self.up(x4_0)], dim=1)) - x2_2 = self.conv2_2(torch.cat([self.attention2_2(x2_0, x3_1), self.up(x3_1)], dim=1)) - x1_3 = self.conv1_3(torch.cat([self.attention1_3(x1_0, x2_2), self.up(x2_2)], dim=1)) - x0_4 = self.conv0_4(torch.cat([self.attention0_4(x0_0, x1_3), self.up(x1_3)], dim=1)) - else: - x0_4 = self.conv0_4(torch.cat([x0_0, self.up(x1_0)], dim=1)) - - # 计算每个类别上的得分, x0_4:[16, 32, 128, 128] - output = self.final(x0_4) - - return output # [16, 3, 128, 128] - - def __str__(self): - ret = 'U-Net' - if self.is_recurrent_residual: - ret = 'R2U-Net' - if self.is_attention: - ret = 'Attention ' + ret - return ret \ No newline at end of file diff --git a/LogicImplement/models/xnet.py b/LogicImplement/models/xnet.py deleted file mode 100644 index d48b9c1..0000000 --- a/LogicImplement/models/xnet.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -import torch -from torch import nn -from .network_blocks import SingleConvBlock - - -class XNet(nn.Module): - """用于数据量小的X-Ray图像分割的网络,架构上是将2个SegNet拼接了起来,并添加了一些skip connection, - https://github.com/JosephPB/XNet,https://arxiv.org/abs/1812.00548 - """ - - def __init__(self, num_classes, in_channels): - super().__init__() - # TODO: nb_filters需要确认下 - nb_filters = [32, 64, 128, 256] - - # 标准的2倍上采样和下采样,因为没有可以学习的参数,可以共享 - self.down = nn.MaxPool2d(kernel_size=2, stride=2) - self.up = nn.UpsamplingBilinear2d(scale_factor=2) - - # 第一个SegNet - self.conv0 = SingleConvBlock(in_channels, nb_filters[0]) - self.conv1 = SingleConvBlock(nb_filters[0], nb_filters[1]) - self.conv2 = SingleConvBlock(nb_filters[1], nb_filters[2]) - self.conv3 = nn.Sequential( - SingleConvBlock(nb_filters[2], nb_filters[3]), - SingleConvBlock(nb_filters[3], nb_filters[3]) - ) - self.conv4 = SingleConvBlock(nb_filters[3] + nb_filters[2], nb_filters[2]) - self.conv5 = SingleConvBlock(nb_filters[2] + nb_filters[1], nb_filters[1]) - - # 第2个SegNet - self.conv6 = SingleConvBlock(nb_filters[1], nb_filters[1]) - self.conv7 = SingleConvBlock(nb_filters[1], nb_filters[2]) - self.conv8 = nn.Sequential( - SingleConvBlock(nb_filters[2], nb_filters[3]), - SingleConvBlock(nb_filters[3], nb_filters[3]) - ) - self.conv9 = SingleConvBlock(nb_filters[3] + nb_filters[2], nb_filters[2]) - self.conv10 = SingleConvBlock(nb_filters[2] + nb_filters[1], nb_filters[1]) - self.conv11 = SingleConvBlock(nb_filters[1] + nb_filters[0], nb_filters[0]) - - # 最后接一个Conv计算在所有类别上的分数 - self.final = nn.Conv2d(nb_filters[0], num_classes, kernel_size=1, stride=1) - - def forward(self, input): - # 第一个SegNet - x0 = self.conv0(input) - x1 = self.conv1(self.down(x0)) - x2 = self.conv2(self.down(x1)) - x3 = self.conv3(self.down(x2)) - x4 = self.conv4(torch.cat([x2, self.up(x3)], dim=1)) - x5 = self.conv5(torch.cat([x1, self.up(x4)], dim=1)) - - # 第2个SegNet - x6 = self.conv6(x5) - x7 = self.conv7(self.down(x6)) - x8 = self.conv8(self.down(x7)) - x9 = self.conv9(torch.cat([x7, self.up(x8)], dim=1)) - x10 = self.conv10(torch.cat([x6, self.up(x9)], dim=1)) - x11 = self.conv11(torch.cat([x0, self.up(x10)], dim=1)) - - # 计算每个类别上的得分 - output = self.final(x11) - - return output - - def __str__(self): - return 'X-Net' diff --git a/LogicImplement/train.py b/LogicImplement/train.py deleted file mode 100644 index d727f4e..0000000 --- a/LogicImplement/train.py +++ /dev/null @@ -1,307 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -import os -# -os.environ["CUDA_VISIBLE_DEVICES"] = "0" - -import torch -import time -from torch.utils.data import DataLoader -from torch.utils.tensorboard import SummaryWriter -from models import unet, nested_unet, loss_function, xnet, nfn_plus, lovasz_losses, unet_tiny -from datasets import lung -from torchvision import transforms -import os -import argparse -import numpy as np -from utils.metrics import compute_metrics -from utils.tools import create_directory - -device = torch.device("cuda" if torch.cuda.is_available() else "cpu") -# 命令行参数设置 -parser = argparse.ArgumentParser() -# 模型选择 -parser.add_argument('--model', type=str, default='unet', choices=['unet', 'r2unet', 'attention_unet', 'attention_r2unet', 'nested_unet', - 'xnet', 'nfn_plus']) -# 数据集选择 -parser.add_argument('--dataset', type=str, default='lung', choices=['lung']) -# 损失选择 -parser.add_argument('--loss', type=str, default='Dice', choices=['DiceBCE', 'CE', 'SCE', 'Dice', 'Lovasz']) -# tools.py 增强 -parser.add_argument('--noisy_rate', type=float, choices=[0.2, 0.3, 0.4]) -parser.add_argument('--noisy_type', type=str, choices=['sy', 'asy']) - -parser.add_argument('--checkpoint', type=str, default=None) -parser.add_argument('--gpu', type=str, default='0', choices=['0', '1']) -parser.add_argument('--parallel', type=str, default='False', choices=['True', 'False']) -parser.add_argument('--num_workers', type=int, default=0, choices=list(range(17))) -parser.add_argument('--epoch', type=int, default=300) -parser.add_argument('--batch_size', type=int, default=2) -# Learning rate -parser.add_argument('--lr', type=float, default=1e-4) -# 学习率衰减 -parser.add_argument('--weight_decay', type=float, default=0) -parser.add_argument('--print_frequency', type=int, default=4) -parser.add_argument('--save_frequency', type=int, default=4) -args = parser.parse_args() - -# 其他准备 -BASE_PATH = r'C:\Users\29185\Desktop\大创-肺部疾病检测\TianChiCTSeg' -format = '{}_{}_{}'.format(args.dataset, args.model, args.loss) -# ? -loss_weights = False -if loss_weights: - format += "_unet_dice_debug" - -log_path = os.path.join(BASE_PATH, 'log', format) -os.makedirs(log_path, exist_ok=True) -checkpoint_path_prefix = os.path.join(BASE_PATH, 'checkpoint', format) -os.makedirs(checkpoint_path_prefix, exist_ok=True) - - -# DEVICE = 'cuda' -DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") - -# 加载数据 -print('Loading data...') -# 选择数据集 -if args.dataset == 'lung': - dataset = lung.Lung -else: - print('数据集异常') - pass - -# 对image和mask进行resize -transform = transforms.Compose([transforms.ToTensor()]) -target_transform = transforms.Compose([transforms.ToTensor()]) - -# noisy_chaos可以设置噪声率和噪声类型 -if args.dataset == 'lung': - train_data = dataset(mode='train', transform=transform, target_transform=target_transform, - BASE_PATH=r"C:\Users\29185\Desktop\大创-肺部疾病检测\TianChiCTSeg\data\train") - val_data = dataset(mode='val', transform=transform, target_transform=target_transform, - BASE_PATH=r"C:\Users\29185\Desktop\大创-肺部疾病检测\TianChiCTSeg\data\val") - -train_loader = DataLoader(dataset=train_data, batch_size=args.batch_size, shuffle=True, - num_workers=args.num_workers, pin_memory=True) -val_loader = DataLoader(dataset=val_data, batch_size=args.batch_size, shuffle=False, - num_workers=args.num_workers, pin_memory=True) -print('Create model...') - -# 选择网络模型 -if args.model == 'unet': - net = unet.UNet(num_classes=dataset.NUM_CLASSES, in_channels=dataset.CHANNELS_NUM) -elif args.model == 'r2unet': - net = unet.UNet(num_classes=dataset.NUM_CLASSES, in_channels=dataset.CHANNELS_NUM, is_recurrent_residual=True) -elif args.model == 'attention_unet': - net = unet.UNet(num_classes=dataset.NUM_CLASSES, in_channels=dataset.CHANNELS_NUM, is_attention=True) -elif args.model == 'attention_r2unet': - net = unet.UNet(num_classes=dataset.NUM_CLASSES, in_channels=dataset.CHANNELS_NUM, is_attention=True, - is_recurrent_residual=True) -elif args.model == 'nested_unet': - net = nested_unet.NestedUNet(num_classes=dataset.NUM_CLASSES, in_channels=dataset.CHANNELS_NUM) -elif args.model == 'xnet': - net = xnet.XNet(num_classes=dataset.NUM_CLASSES, in_channels=dataset.CHANNELS_NUM) -elif args.model == 'nfn_plus': - net = nfn_plus.NFNPlus(num_classes=dataset.NUM_CLASSES, in_channels=dataset.CHANNELS_NUM) -elif args.model == 'unet2tiny': - net = unet_tiny.UNet2tiny(num_classes=dataset.NUM_CLASSES, in_channels=dataset.CHANNELS_NUM) -elif args.model == 'unet3tiny': - net = unet_tiny.UNet3tiny(num_classes=dataset.NUM_CLASSES, in_channels=dataset.CHANNELS_NUM) -elif args.model == 'unet1tiny': - net = unet_tiny.UNet1tiny(num_classes=dataset.NUM_CLASSES, in_channels=dataset.CHANNELS_NUM) - -# 设置优化方法和损失函数 -# optimizer = torch.optim.SGD(net.parameters(), lr=args.lr, momentum=0.9, weight_decay=args.weight_decay) -optimizer = torch.optim.Adam(net.parameters(), lr=args.lr) - - -# 选择损失函数 -if args.loss == 'DiceBCE': - criterion = loss_function.DiceAndBCELoss(dataset.NUM_CLASSES) -elif args.loss == 'CE': - criterion = torch.nn.CrossEntropyLoss() -elif args.loss == 'SCE': - criterion = loss_function.SCELoss(dataset.NUM_CLASSES, alpha=1, beta=1) -elif args.loss == 'Dice': - criterion = loss_function.SoftDiceLoss(dataset.NUM_CLASSES) -elif args.loss == 'Lovasz': - criterion = loss_function.LovaszLoss() - - -print('<================== Parameters ==================>') -print('model: {}'.format(net)) -print('dataset: {}(training={}, validation={})'.format(train_data, len(train_data), len(val_data))) -print('batch_size: {}'.format(args.batch_size)) -print('batch_num: {}'.format(len(train_loader))) -print('epoch: {}'.format(args.epoch)) -print('loss_function: {}'.format(criterion)) -print('optimizer: {}'.format(optimizer)) -print('tensorboard_log_path: {}'.format(log_path)) -print('<================================================>') - - -# 判断是否使用多GPU运行 -if args.parallel == 'True': - print('Use DataParallel.') - net = torch.nn.DataParallel(net) -# GPU or CPU -net = net.to(device) - -start_epoch = 0 -temp = 0 -# 加载模型 -if args.checkpoint is not None: - checkpoint_data = torch.load(args.checkpoint) - print('**** Load model and optimizer data from {} ****'.format(args.checkpoint)) - - # 加载模型和优化器的数据 - net.load_state_dict(checkpoint_data['model_state_dict']) - optimizer.load_state_dict(checkpoint_data['optimizer_state_dict']) - - # 加载上次训练的最后一个epoch和打印的最后一个temp,这里作为起点,需要在之前的基础上加1 - start_epoch = checkpoint_data['epoch'] + 1 - temp = checkpoint_data['temp'] + 1 - - args.epoch += start_epoch - # temp = (len(train_loader) // args.print_frequency) * start_epoch + 1 - -else: - # 如果重新开始训练,则删除原来的log并新建 - create_directory(log_path) - -writer = SummaryWriter(log_dir=log_path, flush_secs=30) -# 训练与验证的过程 -print('Start training...') -for epoch in range(start_epoch, args.epoch): - # 训练 - loss_all = [] - predictions_all = [] - labels_all = [] - print('-------------------------------------- Training {} --------------------------------------'.format(epoch + 1)) - net.train() - for index, data in enumerate(train_loader): - inputs, labels = data - inputs, labels = inputs.to(device), labels.to(device) - optimizer.zero_grad() - outputs = net(inputs) # - loss = 0 - # 如果使用deep supervision,返回1个list(包含多个输出),计算每个输出的loss,最后求平均 - if isinstance(outputs, list): - for out in outputs: - loss += criterion(out, labels.long()) - loss /= len(outputs) - else: - loss = criterion(outputs, labels.long()) - # 计算在该批次上的平均损失函数 - loss /= inputs.size(0) - - # 更新网络参数 - loss.backward() - optimizer.step() - - loss_all.append(loss.item()) - - if isinstance(outputs, list): - # 若使用deep supervision,用最后的输出来进行预测 - predictions = torch.max(outputs[-1], dim=1)[1].cpu().numpy().astype(np.int) - else: - # 将概率最大的类别作为预测的类别 - predictions = torch.max(outputs, dim=1)[1].cpu().numpy().astype(np.int) - - labels = labels.cpu().numpy().astype(np.int) - - predictions_all.append(predictions) - labels_all.append(labels) - - if (index + 1) % args.print_frequency == 0: - # 计算打印间隔的平均损失函数 - avg_loss = np.mean(loss_all) - loss_all = [] - - writer.add_scalar('train/loss', avg_loss, temp) - temp += 1 - - print("Training: Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} ".format( - epoch + 1, args.epoch, index + 1, len(train_loader), avg_loss)) - - # 使用混淆矩阵计算语义分割中的指标 - iou, miou, dsc, mdsc, ac, pc, mpc, se, mse, sp, msp, f1, mf1 = compute_metrics(predictions_all, labels_all, - dataset.NUM_CLASSES) - - writer.add_scalars('train/metrics', dict(miou=miou, mdsc=mdsc, mpc=mpc, ac=ac, mse=mse, msp=msp, mf1=mf1), epoch) - - print('Training: MIoU: {:.4f}, MDSC: {:.4f}, MPC: {:.4f}, AC: {:.4f}, MSE: {:.4f}, MSP: {:.4f}, MF1: {:.4f}'.format( - miou, mdsc, mpc, ac, mse, msp, mf1 - )) - - # 验证 - loss_all = [] - predictions_all = [] - labels_all = [] - - print('-------------------------------------- Validation {} ------------------------------------'.format(epoch + 1)) - - net.eval() - with torch.no_grad(): - for _, data in enumerate(val_loader): - inputs, labels = data - inputs, labels = inputs.to(device), labels.to(device) - - outputs = net(inputs) - - loss = 0 - # 如果使用deep supervision,返回1个list(包含多个输出),计算每个输出的loss,最后求平均 - if isinstance(outputs, list): - for out in outputs: - loss += criterion(out, labels.long()) - loss /= len(outputs) - else: - loss = criterion(outputs, labels.long()) - # 计算在该批次上的平均损失函数 - loss /= inputs.size(0) - - loss_all.append(loss.item()) - - if isinstance(outputs, list): - # 若使用deep supervision,用最后一个输出来进行预测 - predictions = torch.max(outputs[-1], dim=1)[1].cpu().numpy().astype(np.int) - else: - # 将概率最大的类别作为预测的类别 - predictions = torch.max(outputs, dim=1)[1].cpu().numpy().astype(np.int) - labels = labels.cpu().numpy().astype(np.int) - - predictions_all.append(predictions) - labels_all.append(labels) - - # 使用混淆矩阵计算语义分割中的指标 - iou, miou, dsc, mdsc, ac, pc, mpc, se, mse, sp, msp, f1, mf1 = compute_metrics(predictions_all, labels_all, - dataset.NUM_CLASSES) - avg_loss = np.mean(loss_all) - - writer.add_scalar('val/loss', avg_loss, epoch) - writer.add_scalars('val/metrics', dict(miou=miou, mdsc=mdsc, mpc=mpc, ac=ac, mse=mse, msp=msp, mf1=mf1), epoch) - - # 绘制每个类别的IoU - temp_dict = {'miou': miou} - for i in range(dataset.NUM_CLASSES): - temp_dict['class{}'.format(i)] = iou[i] - writer.add_scalars('val/class_iou', temp_dict, epoch) - - print('Training: MIoU: {:.4f}, MDSC: {:.4f}, MPC: {:.4f}, AC: {:.4f}, MSE: {:.4f}, MSP: {:.4f}, MF1: {:.4f}'.format( - miou, mdsc, mpc, ac, mse, msp, mf1 - )) - - # 保存模型参数和优化器参数 - if (epoch + 1) % args.save_frequency == 0: - checkpoint_path = '{}_{}_{}.pkl'.format(format, time.strftime('%m%d_%H%M', time.localtime()), epoch) - # save_checkpoint_path = checkpoint_path_prefix + '/' + checkpoint_path - save_checkpoint_path = os.path.join(checkpoint_path_prefix, checkpoint_path) - torch.save({ - 'is_parallel': args.parallel, - 'epoch': epoch, - 'temp': temp, - 'model_state_dict': net.state_dict(), - 'optimizer_state_dict': optimizer.state_dict()}, - save_checkpoint_path) - print('Save model at {}.'.format(save_checkpoint_path)) diff --git a/LogicImplement/utils/__init__.py b/LogicImplement/utils/__init__.py deleted file mode 100644 index 67f768c..0000000 --- a/LogicImplement/utils/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* diff --git a/LogicImplement/utils/__pycache__/__init__.cpython-38.pyc b/LogicImplement/utils/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 4d3d8f5..0000000 Binary files a/LogicImplement/utils/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/utils/__pycache__/metrics.cpython-38.pyc b/LogicImplement/utils/__pycache__/metrics.cpython-38.pyc deleted file mode 100644 index 82e4f0c..0000000 Binary files a/LogicImplement/utils/__pycache__/metrics.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/utils/__pycache__/tools.cpython-38.pyc b/LogicImplement/utils/__pycache__/tools.cpython-38.pyc deleted file mode 100644 index 4d75706..0000000 Binary files a/LogicImplement/utils/__pycache__/tools.cpython-38.pyc and /dev/null differ diff --git a/LogicImplement/utils/metrics.py b/LogicImplement/utils/metrics.py deleted file mode 100644 index 5b1ed7a..0000000 --- a/LogicImplement/utils/metrics.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -import numpy as np - - -def compute_confusion_matrix(predictions_all, labels_all, num_classes): - """根据每批数据的预测和原始的label,计算混淆矩阵""" - - confusion_matrix = np.zeros((num_classes, num_classes), dtype=np.int) - for predictions, labels in zip(predictions_all, labels_all): - predictions = predictions.flatten() - labels = labels.flatten() - - # np.bincount统计每个值的出现次数 - temp = np.bincount(num_classes * labels + predictions, minlength=num_classes ** 2).reshape( - (num_classes, num_classes)) - - confusion_matrix += temp - - return confusion_matrix - - -def compute_metrics(predictions_all, labels_all, num_classes): - """计算语义分割中的指标 - https://github.com/ZijunDeng/pytorch-semantic-segmentation/blob/master/utils/misc.py, - https://github.com/LeeJunHyun/Image_Segmentation, - https://www.cnblogs.com/Trevo/p/11795503.html, - https://www.aiuai.cn/aifarm1330.html""" - - confusion_matrix = compute_confusion_matrix(predictions_all, labels_all, num_classes) - - # 每个类别的交并比和平均的交并比 - # IoU=TP/(TP+FP+FN) - iou = np.diag(confusion_matrix) / ( - confusion_matrix.sum(axis=1) + confusion_matrix.sum(axis=0) - np.diag(confusion_matrix)) - miou = np.nanmean(iou) - - # 每个类别的Dice相似系数和平均的Dice相似系数 - # DSC=2*TP/(TP+FN+TP+FP) - dsc = 2 * np.diag(confusion_matrix) / (confusion_matrix.sum(axis=1) + confusion_matrix.sum(axis=0)) - mdsc = np.nanmean(dsc) - - # 所有像素总的准确率、每个类别的像素准确率、平均的像素准确率 - # AC=(TP+TN)/(TP+TN+FP+FN) - # PC=TP/(TP+FP) - ac = np.diag(confusion_matrix).sum() / confusion_matrix.sum() - pc = np.diag(confusion_matrix) / confusion_matrix.sum(axis=0) - mpc = np.nanmean(pc) - - # 敏感性,Sensitivity - # SE=TP/(TP+FN) - se = np.diag(confusion_matrix) / confusion_matrix.sum(axis=1) - mse = np.nanmean(se) - - # 特异性,Specificity - # SP=TN/(TN+FP) - sp = [(np.diag(confusion_matrix).sum() - confusion_matrix[i, i]) / - (np.diag(confusion_matrix).sum() + confusion_matrix.sum(axis=0)[i] - 2 * confusion_matrix[i, i]) - for i in range(len(confusion_matrix))] - msp = np.nanmean(sp) - - # F1分数,F1 score - # f1=2*PC*SE/(PC+SE) - f1 = 2 * pc * se / (pc + se) - mf1 = np.nanmean(f1) - - print('Confusion Matrix:') - print(confusion_matrix) - print('IoU: {}'.format(iou)) - print('DSC: {}'.format(dsc)) - print('PC: {}'.format(pc)) - print('SE: {}'.format(se)) - print('SP: {}'.format(sp)) - print('F1: {}'.format(f1)) - - return iou, miou, dsc, mdsc, ac, pc, mpc, se, mse, sp, msp, f1, mf1 diff --git a/LogicImplement/utils/tools.py b/LogicImplement/utils/tools.py deleted file mode 100644 index 40befbd..0000000 --- a/LogicImplement/utils/tools.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -* -from torch import nn -import json -import os -import torch -import shutil -import numpy as np -from PIL import ImageDraw, Image -import random - - -def generate_noisy_indexes(seed, dataset_size, noisy_rate): - """生成需要加噪的图像的index""" - - num = int(dataset_size * noisy_rate) - random.seed(seed) - indexes = random.sample(range(0, dataset_size), num) - return indexes - - -def add_noisy_triangle_label(mask): - """在mask上添加噪声标注""" - - while True: - noisy_image = Image.fromarray(np.zeros(mask.size, dtype=np.uint8)) - # 生成随机的三角形的顶点坐标 - noisy_points = np.random.randint(0, 256, (3, 2)) - # 三角形的跨度不能太大 - if (noisy_points.max(axis=0) - noisy_points.min(axis=0)).max() > 75: - continue - - noisy_points = noisy_points.flatten() - noisy_points = tuple(noisy_points) - ImageDraw.Draw(noisy_image).polygon(noisy_points, fill=255) - - # 噪声标签的面积不能太大 - noisy_array = np.asarray(noisy_image) / 255 - if noisy_array.sum() <= 1500 and noisy_array.sum() >= 1000: - # 在原图上添加噪声 - ImageDraw.Draw(mask).polygon(noisy_points, fill=255) - break - - return mask - - -def create_directory(dir_path): - """创建目录,若存在,则删除后再创建""" - - if os.path.exists(dir_path): - shutil.rmtree(dir_path) - print('Remove {}'.format(dir_path)) - os.mkdir(dir_path) - print('Create {}'.format(dir_path)) - - -def process_binary_mask_tensor(mask): - """对生成的Tensor类型的mask进行处理""" - - assert isinstance(mask, torch.Tensor), '输入的不是Tensor类型' - - # ToTensor()会自动给数据添加一个channel维度,因为是类别标签,不需要这个维度 - if len(mask.shape) == 3: - mask = torch.squeeze(mask, dim=0) - - mask = mask.to(torch.uint8) - mask[mask != 0] = 1 - - return mask - - -def process_multiple_mask_tensor(mask, color_map): - """对生成的Tensor类型的mask进行处理""" - - assert isinstance(mask, torch.Tensor), '输入的不是Tensor类型' - - # ToTensor()会自动给数据添加一个channel维度,因为是类别标签,不需要这个维度 - if len(mask.shape) == 3: - mask = torch.squeeze(mask, dim=0) - - # 如果mask被Resize,修改其中生成的噪声标签,并将数据类型转换为uint8,将其中的255替换为1 - if torch.max(mask).item() <= 1: - # ToTensor()会将数据进行max-min归一化,这里进行还原 - mask = (mask * 255).to(torch.uint8) - - # 记录噪声标记的坐标 - noise_indexes = torch.ones_like(mask, dtype=torch.bool) - - # 去除噪声标记 - for color_value, class_id in color_map.items(): - noise_indexes = noise_indexes & (mask != color_value) - mask[noise_indexes] = 0 - - # 将对应的类别的颜色值替换为其类别id - for color_value, class_id in color_map.items(): - mask[mask == color_value] = class_id - - # 因为Resize使用线性插值的缘故,会产生一些少量的现有类别的标记,但是其数量很少,将其过滤掉 - for class_id in color_map.values(): - if torch.sum(mask == class_id).item() <= 10: - mask[mask == class_id] = 0 - - return mask - - -def is_augmented(path): - """从路径判断是否是被增广的数据""" - - augmented_types = ['VerticalFlip', 'HorizontalFlip', 'Transpose', 'RandomRotate90'] - for t in augmented_types: - if path.__contains__(t): - return True - return False - - -def remove_augmented_item(old_items): - """删除被数据增强的数据""" - - new_items = [] - for item in old_items: - if not is_augmented(item['image_path']): - new_items.append(item) - return new_items - - -def make_dataset(mode, base_path, is_contain_augmented_data): - """从指定目录下读取用于training、validation的json文件""" - - assert mode in ['train', 'val'] - - path = os.path.join(base_path, mode) - image_path = os.path.join(path, "image") - mask_path = os.path.join(path, "mask") - - image_list = os.listdir(image_path) - mask_list = os.listdir(mask_path) - return image_list, mask_list - - # json_path = os.path.join(base_path, '{}.json'.format(mode)) - # - # with open(json_path, 'r') as f: - # items = json.load(f) - # - # if not is_contain_augmented_data and mode == 'train': - # items = remove_augmented_item(items) - # - # return items - - -def initialize_weights(model): - """初始化神经网络中的参数""" - - for module in model.modules(): - if isinstance(module, nn.Conv2d) or isinstance(module, nn.Linear): - nn.init.kaiming_normal_(module.weight) - if module.bias is not None: - module.bias.data.zero_() - elif isinstance(module, nn.BatchNorm2d): - module.weight.data.fill_(1) - module.bias.data.zero_()