本周的总结

目录

本周完成的计划

论文阅读

ABSTRACT(摘要)

1 INTRODUCTION(介绍)

2 FULLY CONVOLUTIONAL MIL(全卷积的MIL)

 3 MULTI-CLASS MIL LOSS(多个类别多示例损失)

4 EXPERIMENTS(实验)

 5 DISCUSSION(结论)

SWI切片代码实现

调色板工具

切patch代码

统计各个类别占的比例

DeepLabv3+分割VOC数据集

训练过程和实验效果

本周工作总结 


本周完成的计划

  • 读病理组织分割论文《Fully Convolutional Multi-Class Multiple Instance Learning》
  • 学习了将大的病理图像和对应标注切patch的操作
  • 学习了DeepLabv3+模型

论文阅读

Fully Convolutional Multi-Class Multiple Instance Learning(全卷积多示例学习)

ABSTRACT(摘要)

多实例学习(MIL)可以通过削弱所需的监督程度来减少在语义分割等任务中的高昂代价的标注的需要。我们提出了一种新的基于完全卷积网络的多类语义分割学习的MIL公式。在这种情况下,我们试图从弱图像级标签中学习语义分割模型。该模型被端到端地训练以联合优化表示,同时消除像素-图像标签分配的歧义。完全卷积训练接受任何大小的输入,不需要对象建议预处理,并提供像素级损失图来选择潜在实例。我们的多类MIL Loss利用了具有多个标签的图像提供的进一步监督。我们通过对Pascal VOC分割挑战的初步实验对该方法进行了评估。

1 INTRODUCTION(介绍)

在这项工作中,我们提出了一种新的具有完全卷积网络(FCN)的多示例学习(MIL)框架其任务是从仅表示对象存在或不存在的弱图像级别标签中学习像素级语义分割。未居中的已标记对象或包含多个对象的图像会使问题更加困难。本文的主要工作是通过多实例学习来驱动ConvNet表示和像素分类器的联合学习。完全卷积训练在每个像素端到端地学习模型。为了从图像标签中学习分割模型,我们将每幅图像投射为一个bag像素级实例,并定义了MIL的像素级、多类自适应的损失。

我们研究了以下想法,并进行了初步实验:

  • 我们在全卷积网络中结合端到端表示学习来执行MIL。这消除了实例化实例标签假设的需要。FCN学习和推理可以处理不同大小的图像,而不需要进行变形或目标建议预处理。这使得训练变得简单和快捷。
  • 我们提出了一种受二进制MIL场景启发的多类像素级损失。这试图最大化基于每个像素实例的分类分数,同时利用类间竞争来缩小实例假设的范围。
  • 我们的目标是弱监督图像分割这一未被研究的问题。我们相信像素级的一致性提示有助于消除物体存在的歧义。这样,弱分割可以比边界框包含更多的图像结构。

2 FULLY CONVOLUTIONAL MIL(全卷积的MIL)

对于弱监督的MIL学习,FCN允许有效地选择训练实例。FCN预测所有像素的输出图,并具有所有像素的相应损失图。该损失图可以被屏蔽、重新加权或以其他方式操作,以选择和选择用于计算损失和用于学习的反向传播的实例。

我们使用VGG 16层网络,并按照Long等人的建议将其转成完全卷积形式。通过用相应的卷积替换完全连通的层来进行语义分割。网络根据预先训练的ILSVRC分类器权重进行微调,即预先训练以预测图像级标签。然后,我们在没有初始化最后一层权重(即分类器层)的情况下进行实验。这些初始化在没有MIL微调的情况下充当基线(表中的第1行和第2行)。如果没有图像级别的预训练,模型会快速收敛到所有背景。语义分割需要一个背景类,但是分类任务没有背景类;我们简单地将背景分类器权重初始化为零。

 3 MULTI-CLASS MIL LOSS(多个类别多示例损失)

我们将多类别MIL损失定义为在最大预测值下计算的多类别损失由FCN产生的输出图实现,即对于任意大小的图像,FCN输出相应大小的每个类别(包括背景)的热图。我们在图像和背景中存在的类的粗略热图中确定最大得分像素。然后,仅在这些粗点上计算损失,并通过网络反向传播

 \mathbb{I}表示的是输入图像,L_{I}是图像对应的标签,\hat{p}_{l}(x,y)是第l^{^{th}}张标签在\left ( x,y \right )位置输出的热图

对于一张图片,输进分割网络,得到的是一个(N,H,W)的分割Maps,图片对于每一个class的预测被存储为的一个feature map。

这个Loss取每一个map上的最大的值,计算公式如上图,然后,对这个最大的分值进行约束,限制该类别在图片中的存在与否,若图片的标签中表示存在的类,则这个分值接近于1,若不存在则接近于0。

简单一点讲,就是通过限制feature maps上的最大值趋近1来促使应该存在的类的预测值大,限制最大值趋近0来促使应该不存在的类的预测值小。

4 EXPERIMENTS(实验)

所有结果都来自Pascal VOC分割挑战比赛,结果如下:

 5 DISCUSSION(结论)

本文提出了一种受两类别的MIL启发的多类像素级损失联合多实例表示学习模型。该模型端到端学习,作为一个完全卷积网络,用于弱监督语义分割任务。它排除了任何类型的或实例假设机制的需要,推理速度快(≈1/5秒)。这些结果令人鼓舞,而且还可以进一步改进。目前,我们粗粒度的输出仅是线性插值出来的,如果使用条件随机场正则化或超像素投影等方法可以细化预测的结果。

SWI切片代码实现

因为病理图像都是很大像素的,为了重复利用好有限的数据,一般都会对大的病理图像进行切片操作。老师发的数据集共有151张有mask的病理图像。

分割前的每张病理大图像,3394*2467像素和其对应的mask(使用染色板上色)

 分割后的patch,每个patch256*256像素

 

调色板工具

import os
import glob
import logging
import time
from shutil import copyfile
from multiprocessing import Pool, Value, Lock
from PIL import Image
import numpy as np
import torch


LABEL_PATH = 'D:\\bcss\\masks\\'  # 第五个路径就是放标签mask的地方,就是哪些黑黑的图
PALLET_LABEL_PATH = "D:\\bcss\\palette_masks\\"

import cv2
import os
import numpy as np
from PIL import Image

import cv2
import os
import numpy as np
from PIL import Image


def colorful(img, save_path):

    '''
    img:需要上色的图片
    save_path:存储路径
    '''

    # print(np.unique(img))
    img = Image.fromarray(img)  # 将图像从numpy的数据格式转为PIL中的图像格式
    palette = []
    for i in range(256):
        palette.extend((i, i, i))
    palette[:3 * 22] = np.array([[0, 0, 0],  # outside_roi	0 黑 不关心的地方
                                 [128, 0, 0],  # tumor	1 褐红 肿瘤 # 关注
                                 [0, 128, 0],  # stroma	2 绿 基质 # 关注
                                 [128, 128, 0],  # lymphocytic_infiltrate 3 淡褐 浸润 # 关注
                                 [0, 0, 128],  # necrosis_or_debris	4 藏青 坏死 # 关注
                                 [128, 0, 128],  # glandular_secretions	5 紫 腺分泌物
                                 [0, 128, 128],  # blood 6 蓝绿 血
                                 [128, 128, 128],  # exclude 7 灰 排除
                                 [64, 0, 0],  # metaplasia_NOS 8  化生(metaplasia) 无色名 有点暗红褐色
                                 [192, 0, 0],  # fat 9 脂肪 无色名 有点浅红色
                                 [64, 128, 0],  # plasma_cells 10 血浆细胞 无色名 有点绿色
                                 [192, 128, 0],  # other_immune_infiltrate	11 其它免疫浸润 无色名 有点淡橙色
                                 [64, 0, 128],  # mucoid_material 12 粘液样物质 无色名 有点蓝紫色
                                 [192, 0, 128],  # normal_acinus_or_duct 13 正常腺泡或导管  无色名 深粉色  # 关注
                                 [64, 128, 128],  # lymphatics	14 淋巴系统 无色名 有点蓝绿色
                                 [192, 128, 128],  # undetermined	15 未确定的 无色名 有点淡粉
                                 [0, 64, 0],  # nerve	16 神经  无色名 深绿
                                 [128, 64, 0],  # skin_adnexa	17 皮肤附件 无色名 有点褐色
                                 [0, 192, 0],  # blood_vessel	18 血管 无色名 浅绿
                                 [128, 192, 0],  # angioinvasion	19 血管侵犯 无色名 有点淡深绿
                                 [0, 64, 128],  # dcis	20  乳房原位癌 无色名 有点淡深蓝
                                 [224, 128, 64],  # other	21 其它 无色名 有点淡橙色
                                 ], dtype='uint8').flatten()

    img.putpalette(palette)
    print(np.unique(img))
    img.save(save_path)



def run():
    print("===================================================")
    # 黑黑图的路径
    wsi_mask_paths = glob.glob(os.path.join(LABEL_PATH, '*.png'))
    num = 0
    for wsi_mask_path in wsi_mask_paths:
        # obtain the wsi path

        name = wsi_mask_path.split('\\')[-1]
        pid = name[:-4]
        print(name)
        print('pid', pid)
        num += 1
        print("处理第{}张mask图像pid = {}".format(num , pid))
        print("num = ",num)


        # 打开黑黑图
        img_label_mask = Image.open(wsi_mask_path)
        img_label_mask = np.array(img_label_mask)
        colorful(img_label_mask, os.path.join(PALLET_LABEL_PATH, str(pid))+ '.png')


if __name__ == '__main__':
    run()

切patch代码

import os
import glob
import logging
import time
from shutil import copyfile
from multiprocessing import Pool, Value, Lock
from PIL import Image
import numpy as np
import torch
from convert_palette_imgs import colorful


def run2():
    # 路径放切下来的标签patch
    if not os.path.exists(TRAIN_PATCH_PATH):
        os.mkdir(TRAIN_PATCH_PATH)

    # TRAIN_PATH:路径就是放原图 通过原图路径找到所有原图
    wsi_paths = glob.glob(os.path.join(TRAIN_PATH, '*.png'))
    # 黑黑图的路径
    wsi_mask_paths = glob.glob(os.path.join(MASK_PATH, '*.png'))
    # 彩彩图的路径
    pallet_mask_paths = glob.glob(os.path.join(PALETTE_MASK_PATH, '*.png'))

    cur_patch_cords = []
    patch_name_list = []
    mask_patch_name_list = []
    pallet_mask_patch_name_list = []
    train_data_lib = {}
    # wsi_path每张原图的路径
    num = 0
    for wsi_path,wsi_mask_path,pallet_mask_path in zip(wsi_paths,wsi_mask_paths,pallet_mask_paths):
        # obtain the wsi path
        name = wsi_path.split('\\')[-1]
        pid = name[:-4]
        print(name)
        print('pid',pid)
        num += 1
        print("处理第{}张病理图像pid = {}".format(num , pid))
        print("num = ",num)

        # gen the folder as the pathological number
        # TRAIN_PATCH_PATH:路径放切下来的标签patch
        ID_PATH = TRAIN_PATCH_PATH
        print(ID_PATH)


        # # 打开黑黑图
        # img_label_mask = Image.open(wsi_mask_path)
        # # img_label_mask.mode = L
        # # print("img_label_mask.mode = ",img_label_mask.mode)
        # img_label_mask = np.array(img_label_mask)

        # 打开彩彩图
        img_pallet_label_mask = Image.open(pallet_mask_path)
        img_pallet_label_mask = np.array(img_pallet_label_mask)


        # open the slide image 打开病理图像
        img = Image.open(wsi_path)
        # MASK_PATH:mask图
        # img_mask = Image.open(MASK_PATH+pid+'_mask.png')
        img_mask = Image.open(MASK_PATH+pid+'.png')
        width,height = img.size
        print(width)
        img = np.array(img)
        img_mask = np.array(img_mask)
        # gen the coordinates (x,y)
        dd1 = []
        dd2 = []

        for i in range(0, width - patch_size, 128):
            dd1.append(i)
        for j in range(0, height - patch_size, 128):
            dd2.append(j)
        print("dd1 = ", dd1)
        print("dd2 = ", dd2)
        print("len(dd1) = ",len(dd1)," ---- "," len(dd2) = ",len(dd2),"  -----  ","dd1*dd2 = ",len(dd1) * len(dd2))
        # if width % patch_size / patch_size > 0.5:
        #     dd1.append(width - patch_size)
        #
        # if height % patch_size / patch_size > 0.5:
        #     dd2.append(height - patch_size)

        # gen the patches as the coordinates and save them
        for ii in range(len(dd1)):
            for jj in range(len(dd2)):
                # s = 0.6 * 256 * 256 * 255
                # if np.sum(img_mask[int(dd1[ii]):int(dd1[ii])+patch_size, int(dd2[jj]):int(dd2[jj])+patch_size]) > s:
                wsi_pallet_mask_patch = img_pallet_label_mask[int(dd1[ii]):int(dd1[ii]) + patch_size, int(dd2[jj]):int(dd2[jj]) + patch_size]
                # wsi_mask_patch = img_label_mask[int(dd1[ii]):int(dd1[ii]) + patch_size, int(dd2[jj]):int(dd2[jj]) + patch_size]
                img_patch = img[int(dd1[ii]):int(dd1[ii]) + patch_size, int(dd2[jj]):int(dd2[jj]) + patch_size]
                img_mask_patch = img_mask[int(dd1[ii]):int(dd1[ii])+patch_size, int(dd2[jj]):int(dd2[jj])+patch_size]
                # print(os.path.join(ID_PATH, str(pid) + '_' + str(dd1[ii]) + '_' + str(dd2[jj]) + '.png'))
                if img_patch.shape[0] == 256 & img_patch.shape[1] == 256:
                # print("img_patch.shape = ",img_patch.shape)
                    wsi_pallet_mask_patch = Image.fromarray(wsi_pallet_mask_patch)
                    # wsi_mask_patch = Image.fromarray(wsi_mask_patch)
                    img_patch=Image.fromarray(img_patch)
                    img_mask_patch = Image.fromarray(img_mask_patch)

                    if not os.path.exists(os.path.join(PALETTE_MASK_PATCH_PATH, str(pid))):
                        os.mkdir(os.path.join(PALETTE_MASK_PATCH_PATH, str(pid)))
                    colorful(np.array(wsi_pallet_mask_patch), os.path.join(PALETTE_MASK_PATCH_PATH, str(pid) +'\\'+ str(pid)+'_' + str(dd1[ii]) + '_'+ str(dd2[jj]) + '.png'))
                    # wsi_pallet_mask_patch.save(os.path.join(PALLET_MASK_PATCH_PATH, str(pid) +'\\'+ str(pid)+'_' + str(dd1[ii]) + '_'+ str(dd2[jj]) + '.png'))
                    pallet_mask_patch_name_list.append(os.path.join(PALETTE_MASK_PATCH_PATH, str(pid) + '_' + str(dd1[ii]) + '_' + str(dd2[jj]) + '.png'))

                    # if not os.path.exists(os.path.join(LABEL_PATCH_PATH, str(pid))):
                    #     os.mkdir(os.path.join(LABEL_PATCH_PATH, str(pid)))
                    # wsi_mask_patch.save(os.path.join(LABEL_PATCH_PATH, str(pid) +'\\'+ str(pid)+'_' + str(dd1[ii]) + '_'+ str(dd2[jj]) + '.png'))
                    # mask_patch_name_list.append(os.path.join(LABEL_PATCH_PATH, str(pid) + '_' + str(dd1[ii]) + '_' + str(dd2[jj]) + '.png'))

                    if not os.path.exists(os.path.join(ID_PATH, str(pid))):
                        os.mkdir(os.path.join(ID_PATH, str(pid)))
                    img_patch.save(os.path.join(ID_PATH, str(pid) +'\\'+ str(pid)+'_' + str(dd1[ii]) + '_' + str(dd2[jj]) + '.png'))
                    patch_name_list.append(os.path.join(ID_PATH, str(pid) + '_' + str(dd1[ii]) + '_' + str(dd2[jj]) + '.png'))

                    if not os.path.exists(os.path.join(MASK_PATCH_PATH, str(pid))):
                        os.mkdir(os.path.join(MASK_PATCH_PATH, str(pid)))
                    img_mask_patch.save(os.path.join(MASK_PATCH_PATH, str(pid) +'\\'+ str(pid)+ '_' + str(dd1[ii]) + '_'+ str(dd2[jj]) + '.png'))
                    cur_patch_cords.append(os.path.join(MASK_PATCH_PATH, str(pid) + '_' + str(dd1[ii]) + '_' + str(dd2[jj]) + '.png'))
    train_data_lib['mask_patch_name_list'] = mask_patch_name_list
    train_data_lib['mask_patch_name_list'] = mask_patch_name_list
    train_data_lib['patch_name_list']=patch_name_list
    train_data_lib['cur_patch_cords']=cur_patch_cords
    torch.save(train_data_lib, 'D:\\bcss\logs\\train_data_lib.db')





def main():
    run2()
    # img=Image.open('F:\\Oral_HE_IHCregistered_TMA\\train_mask_patch\\1_B-2_real_A_mask_1024_512.png')
    # img = np.array(img)
    # print(np.sum(img))


if __name__ == '__main__':
    main()

统计各个类别占的比例

统计每个病理组织所占的比例,将较少的组织类型合并,并重新切patch

                   

import json
import os
import glob
import logging
import time
from shutil import copyfile
from multiprocessing import Pool, Value, Lock
from PIL import Image
import numpy as np
import torch
import cv2
import matplotlib.pyplot as plt
"""

label	GT_code
outside_roi	0
tumor	1
stroma	2
lymphocytic_infiltrate	3
necrosis_or_debris	4
glandular_secretions	5
blood	6
exclude	7
metaplasia_NOS	8
fat	9
plasma_cells	10
other_immune_infiltrate	11
mucoid_material	12
normal_acinus_or_duct	13
lymphatics	14
undetermined	15
nerve	16
skin_adnexa	17
blood_vessel	18
angioinvasion	19
dcis	20
other	21

"""
LABEL_PATH = "D:\\bcss\\masks\\"
PALETTE_LABEL_PATH = "D:\\bcss\\palette_masks\\"

category_to_code = {"outside_roi":0,"tumor":1,"stroma":2,"lymphocytic_infiltrate":3,"necrosis_or_debris":4,
                            "glandular_secretions":5,"blood":6,"exclude":7,"metaplasia_NOS":8,"fat":9,"plasma_cells":10,
                            "other_immune_infiltrate":11,"mucoid_material":12,"normal_acinus_or_duct":13,"lymphatics":14,
                            "undetermined":15,"nerve":16,"skin_adnexa":17,"blood_vessel":18,"angioinvasion":19,"dcis":20,"other":21}
code_to_category = {0:"outside_roi",1:"tumor",2:"stroma",3:"lymphocytic_infiltrate",4:"necrosis_or_debris",
                    5:"glandular_secretions",6:"blood",7:"exclude",8:"metaplasia_NOS",9:"fat",10:"plasma_cells",
                    11:"other_immune_infiltrate",12:"mucoid_material",13:"normal_acinus_or_duct",14:"lymphatics",
                    15:"undetermined",16:"nerve",17:"skin_adnexa",18:"blood_vessel",19:"angioinvasion",20:"dcis",21:"other"}
all_imgs_category = {}
all_categories = {"outside_roi":0,"tumor":0,"stroma":0,"lymphocytic_infiltrate":0,"necrosis_or_debris":0,
                            "glandular_secretions":0,"blood":0,"exclude":0,"metaplasia_NOS":0,"fat":0,"plasma_cells":0,
                            "other_immune_infiltrate":0,"mucoid_material":0,"normal_acinus_or_duct":0,"lymphatics":0,
                            "undetermined":0,"nerve":0,"skin_adnexa":0,"blood_vessel":0,"angioinvasion":0,"dcis":0,"other":0}
def cal():
    print("=================== start cal ================================")
    # 黑黑图的路径
    wsi_mask_paths = glob.glob(os.path.join(LABEL_PATH, '*.png'))
    wsi_palette_mask_paths = glob.glob(os.path.join(PALETTE_LABEL_PATH, '*.png'))
    print("len(wsi_mask_paths) = ",len(wsi_mask_paths))

    num = 0
    for wsi_mask_path,wsi_palette_mask_path in zip(wsi_mask_paths,wsi_palette_mask_paths):
        # obtain the wsi path
        print("wsi_palette_mask_path = ",wsi_palette_mask_path)
        name = wsi_mask_path.split('\\')[-1]
        print("name = ",name)
        wsi_mask_img = Image.open(wsi_mask_path)
        # img = plt.imread(wsi_palette_mask_path)
        # plt.title(wsi_palette_mask_path)
        # plt.imshow(img)
        # plt.show()
        # num += 1
        # if (num == 3):
        #     break
        wsi_mask_img_array = np.array(wsi_mask_img)
        print("wsi_mask_img_array.shape = ",wsi_mask_img_array.shape)
        h,w = wsi_mask_img_array.shape[0],wsi_mask_img_array.shape[1]
        # print("w = ",w," h = ",h)
        print("wsi_mask_img_array = ",wsi_mask_img_array)
        print("unique = ",np.unique(wsi_mask_img_array))
        print("="*50)
        print("=" * 50)
        # 2465-3392
        pid = name[:-4]

        # 统计每种类别出现的次数
        per_img_catogory = {"outside_roi":0,"tumor":0,"stroma":0,"lymphocytic_infiltrate":0,"necrosis_or_debris":0,
                            "glandular_secretions":0,"blood":0,"exclude":0,"metaplasia_NOS":0,"fat":0,"plasma_cells":0,
                            "other_immune_infiltrate":0,"mucoid_material":0,"normal_acinus_or_duct":0,"lymphatics":0,
                            "undetermined":0,"nerve":0,"skin_adnexa":0,"blood_vessel":0,"angioinvasion":0,"dcis":0,"other":0}

        print("per_img_catogory = ",per_img_catogory)
        for h_j in range(0,h - 1):
            for w_i in range(0, w - 1):
                pixel_mask = wsi_mask_img_array[h_j][w_i]
                for key,value in category_to_code.items():
                    if pixel_mask == value:
                        per_img_catogory[key] += 1
                        break
        # 将每张图片的统计量,加入到总的统计量中
        all_imgs_category[pid] = per_img_catogory

             
        print("per_img_catogory = ",per_img_catogory)
            #     print(pixel_mask,end=" ")
            #     print(str(h_j) + "-" + str(w_i))
            # print()

    # 打印总的统计量
    print("all_imgs_category = ",all_imgs_category)

    # 统计总的图片中各个类别出现的次数
    all_pixel = 0
    for key,value in all_imgs_category.items():
        # key是每张图片的名字 value是图片每个类别的数量
        for cate_name,cate_num in value.items():
            all_categories[cate_name] += cate_num
            all_pixel += cate_num
    all_category_count_percent = {}
    for key,value in all_categories.items():
        all_category_count_percent[key] = value / all_pixel

    # 将信息写入本地json
    with open('./all_imgs_category.json', 'a', encoding='utf8') as fp:
        json.dump(all_imgs_category, fp, ensure_ascii=False)
    with open('./all_categories.json', 'a', encoding='utf8') as fp:
        json.dump(all_categories, fp, ensure_ascii=False)
    with open('./all_category_count_percent.json', 'a', encoding='utf8') as fp:
        json.dump(all_category_count_percent, fp, ensure_ascii=False)
    print("=================== end cal ================================")


def plot_categories(data_path):
    # 读取json文件内容,返回字典格式
    per_category_percent_names = []
    per_category_percent_values = []
    with open(data_path, 'r', encoding='utf8')as fp:
        json_data = json.load(fp)
        print('这是文件中的json数据:', json_data)
        print('这是读取到文件数据的数据类型:', type(json_data))
        for k,v in category_to_code.items():
            per_category_percent_name = k
            per_category_percent_value = json_data[k]

            per_category_percent_names.append(per_category_percent_name)
            per_category_percent_values.append(per_category_percent_value)
            # print(k + "-" + str(per_category_percent_value))
            # plt.figure(figsize=(12,8))
            # plt.plot()
    # 添加修饰的饼图
    # explode = [0, 0.1, 0, ]  # 生成数据,用于突出显示B
    colors = ['#9999ff', '#ff9999', '#7777aa']  # 自定义颜色

    # 中文乱码和坐标轴负号的处理
    plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
    plt.rcParams['axes.unicode_minus'] = False

    # 将横、纵坐标轴标准化处理,确保饼图是一个正圆,否则为椭圆
    plt.figure(figsize=(10, 10))
    plt.axes(aspect='equal')
    # 绘制饼图
    plt.pie(x=per_category_percent_values,  # 绘图数据
            # explode=explode,  # 突出显示B
            labels=per_category_percent_names,  # 添加教育水平标签
            colors=colors,  # 设置饼图的自定义填充色
            autopct='%.1f%%',  # 设置百分比的格式,这里保留一位小数
            pctdistance=0.8,  # 设置百分比标签与圆心的距离
            labeldistance=1.1,  # 设置教育水平标签与圆心的距离
            startangle=180,  # 设置饼图的初始角度
            radius=2,  # 设置饼图的半径
            counterclock=False,  # 是否逆时针,这里设置为顺时针方向
            wedgeprops={'linewidth': 1, 'edgecolor': 'red'},  # 设置饼图内外边界的属性值
            textprops={'fontsize': 10, 'color': 'black'},  # 设置文本标签的属性值
            )
    # plt.savefig('./categories_percents.png', dpi=100)  # 一定放在plt.show()之前


    # 添加图标题
    # plt.title('各类别所占比例')
    # plt.savefig('./categories_percents.jpg', dpi=300)  # 一定放在plt.show()之前
    fig = plt.gcf()
    fig.savefig('./categories_percents.png', format='png', transparent=True, dpi=20, pad_inches=0)
    # 显示图形
    plt.show()

DeepLabv3+分割VOC数据集

import os
import math
import numpy as np
import glob 
import pandas as pd
import matplotlib.pyplot as plt
from PIL import Image
import random
import time
import cv2
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
import torchvision
import torch
from glob import glob
from PIL import Image
from torch import nn
from torch.utils.data import DataLoader
from torchvision.transforms import transforms

from tqdm import tqdm
import numpy as np



root_path = r"D:\zn_code\腹腔多脏器边缘分割\腹腔多脏器边缘分割\代码\VOCdevkit\VOC2007"

images_path = os.path.join(root_path,"JPEGImages")
labels_path = os.path.join(root_path,"SegmentationClass")

print("images_path = ",images_path)
print("labels_path = ",labels_path)

data_img = os.listdir(images_path)
data_label = os.listdir(labels_path)

print("data_img = ",len(data_img))
print("labels_img = ",len(data_label))

data_imgs = []
data_labels = []
for i in range(len(data_label)):
    img_mask = data_label[i]
    img = img_mask.replace('png','jpg')
    data_imgs.append(os.path.join(root_path,"JPEGImages",img))
    data_labels.append(os.path.join(root_path, "SegmentationClass", img_mask))

print("data_imgs = ",len(data_imgs))
print("data_labels = ",len(data_labels))

print(data_imgs[:5])
print(data_labels[:5])

img_transformer=transforms.Compose([
    transforms.Resize((256,256)),
    transforms.ToTensor()
])
label_transformer=transforms.Compose([
    transforms.Resize((256,256)),
])

class BrainMRIdataset(Dataset):
    def __init__(self,img,mask,transformer=None,label_transformer=None):
        """
        :param img: 图片路径
        :param mask: 标签路径
        :param transformer: 对图像进行的变换
        """
        self.img = img
        self.mask = mask
        self.transformer = transformer
        self.label_transformer = label_transformer

    def __getitem__(self,index):
        img_path = self.img[index] # 单个图片路径
        mask_path = self.mask[index] # 单个标签路径
#         print("img_path = ",img_path)
#         print("mask_path = ",mask_path)

        img_open = Image.open(img_path)
        img_tensor = self.transformer(img_open)

        mask_open = Image.open(mask_path)
        mask_img = self.label_transformer(mask_open)
        ##########################################
        #########这一步最关键,有两个坑#############
        #####1.作为分割的label,label必须是二维######
        #####2.作为分割的label,label必须是long类型的#
        mask_np=np.array(mask_img)
        mask_tensor=torch.from_numpy(mask_np)
#         print("mask_tensor.shape = ",mask_tensor.shape)
        mask_tensor = torch.squeeze(mask_tensor).type(torch.long)
#         print("mask_tensor.shape = ",mask_tensor.shape)

        return img_tensor,mask_tensor
    def __len__(self):
        return len(self.img)


s = 2500
train_img = data_imgs[:s]
train_label = data_labels[:s]
test_img = data_imgs[s:]
test_label = data_labels[s:]

# 训练集和数据集
train_data=BrainMRIdataset(train_img,train_label,img_transformer,label_transformer)
test_data=BrainMRIdataset(test_img,test_label,img_transformer,label_transformer)

dl_train=DataLoader(train_data,batch_size=8,shuffle=True)
dl_test=DataLoader(test_data,batch_size=8,shuffle=True)

# 使用pytorch.models提供的deeplabv3_resnet101分割模型
model=torchvision.models.segmentation.deeplabv3_resnet101(pretrained=False,
                                                          progress=True, 
                                                          num_classes=21, 
                                                          aux_loss=None,
                                                          )       

device = torch.device("cuda")
# 创建模型
model=model.to(device)
# 损失函数
loss_fn=nn.CrossEntropyLoss(ignore_index=255)

# 分割效果的评价函数
def Dice(inp, target, eps=1):
	# 抹平了,弄成一维的
    input_flatten = inp.flatten()
    target_flatten = target.flatten()
    # 计算交集中的数量
    overlap = np.sum(input_flatten * target_flatten)
    # 返回值,让值在0和1之间波动
    return np.clip(((2. * overlap) / (np.sum(target_flatten) + np.sum(input_flatten) + eps)), 1e-4, 0.9999)
   

# 训练和测试方法
def fit(epoch, model, trainloader, testloader):
    correct = 0
    total = 0
    running_loss = 0
    epoch_iou = []
    
    train_dice=[]
    test_dice=[]
    
    model.train()
    for x, y in tqdm(testloader):
 
        x, y = x.to(device), y.to(device)
        y_pred = model(x)
        y_pred=y_pred['out']
        loss = loss_fn(y_pred, y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        with torch.no_grad():
            y_pred = torch.argmax(y_pred, dim=1)
            correct += (y_pred == y).sum().item()
            total += y.size(0)
            running_loss += loss.item()
            
            intersection = torch.logical_and(y, y_pred)
            union = torch.logical_or(y, y_pred)
            batch_iou = torch.sum(intersection) / torch.sum(union)
            epoch_iou.append(batch_iou.item())
            
            y_pred_dice = y_pred
            y_pred_dice=y_pred_dice.cpu().numpy()
            ys=y.cpu().numpy()
            dice=Dice(y_pred_dice,ys)
            train_dice.append(dice)
            
            
            
    epoch_loss = running_loss / len(trainloader.dataset)
    epoch_acc = correct / (total*256*256)
        
        
    test_correct = 0
    test_total = 0
    test_running_loss = 0 
    epoch_test_iou = []
    
    model.eval()
    with torch.no_grad():
        for x, y in tqdm(testloader):

            x, y = x.to(device), y.to(device)
            y_pred = model(x)
            y_pred=y_pred['out']
            loss = loss_fn(y_pred, y)
            y_pred = torch.argmax(y_pred, dim=1)
            test_correct += (y_pred == y).sum().item()
            test_total += y.size(0)
            test_running_loss += loss.item()
            
            intersection = torch.logical_and(y, y_pred)
            union = torch.logical_or(y, y_pred)
            batch_iou = torch.sum(intersection) / torch.sum(union)
            epoch_test_iou.append(batch_iou.item())
            
            y_pred_dice = y_pred
            y_pred_dice=y_pred_dice.cpu().numpy()
            ys=y.cpu().numpy()
            dice=Dice(y_pred_dice,ys)
            test_dice.append(dice)
            
    
    epoch_test_loss = test_running_loss / len(testloader.dataset)
    epoch_test_acc = test_correct / (test_total*256*256)
    
    
    print('epoch: ', epoch, 
          'loss: ', round(epoch_loss, 3),
          'accuracy:', round(epoch_acc, 3),
          'IOU:', round(np.mean(epoch_iou), 3),
          'Dice:',round(np.mean(train_dice), 3),
          'test_loss: ', round(epoch_test_loss, 3),
          'test_accuracy:', round(epoch_test_acc, 3),
           'test_iou:', round(np.mean(epoch_test_iou), 3),
          'testDice:',round(np.mean(test_dice), 3)
         )
        
    return epoch_loss, epoch_acc, epoch_test_loss, epoch_test_acc

# 训练50个人epochs
epochs = 50

train_loss = []
train_acc = []
test_loss = []
test_acc = []

for epoch in range(epochs):
    epoch_loss, epoch_acc, epoch_test_loss, epoch_test_acc = fit(epoch,
                                                                 model,
                                                                 dl_train,
                                                                 dl_test)
    train_loss.append(epoch_loss)
    train_acc.append(epoch_acc)
    test_loss.append(epoch_test_loss)
    test_acc.append(epoch_test_acc)

# 绘制训练效果
image, mask = next(iter(dl_test))
image=image.to(device)
model.eval()
pred_mask = model(image)
pred_mask=pred_mask['out']

mask=torch.squeeze(mask)
pred_mask=pred_mask.cpu()

num=3
plt.figure(figsize=(10, 10))
for i in range(num):
    plt.subplot(num, 3, i*num+1)
    plt.imshow(image[i].permute(1,2,0).cpu().numpy())
    plt.subplot(num, 3, i*num+2)
    plt.imshow(mask[i].cpu().numpy())
    plt.subplot(num, 3, i*num+3)

训练过程和实验效果

 

 

本周工作总结 

1.学会了将大像素病理图像和对应标签切成patch,可以充分利用有限的医学图像图像数据训练网络,因为图像的标注数据,像素值差别不是很大,如果直接显示的话,肉眼很难看出来属于什么类别,可以使用调色板将不同像素值染上不同颜色,方便观察

2.读的《Fully Convolutional Multi-Class Multiple Instance Learning》这篇弱监督论文,使用image-level级标签,利用MIL Loss进行pixel级分割,可以取得一定作用。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值