• 赛题链接:常规赛:PALM眼底彩照视盘探测与分割
  • 赛题简介:ISBI2019 PALM眼科挑战赛赛题再现,提供800张眼底彩照训练数据集, 要求选手训练模型完成眼底视盘结构的探测和分割任务。
  • 赛题背景:
    • 本赛题原型为ISBI2019PALM眼科大赛。 近视已成为全球公共卫生负担。在近视患者中,约35%为高度近视。近视导致眼轴长度的延长,可能引起视网膜和脉络膜的病理改变。随着近视屈光度的增加,高度近视将发展为病理性近视,其特点是病理改变的形成:(1)后极,包括镶嵌型眼底、后葡萄肿、视网膜脉络膜变性等;(2)视盘,包括乳头旁萎缩、倾斜等;(3)近视性黄斑,包括漆裂、福氏斑、CNV等。病理性近视对患者造成不可逆的视力损害。因此,早期诊断和定期随访非常重要。
    • 视网膜由黄斑向鼻侧约3mm处有一直径约1.5mm、境界清楚的淡红色圆盘状结构,称为视神经盘,简称视盘。视盘是眼底图像的一个重要特征,对其进行准确、快速地定位与分割对利用眼底图像进行疾病辅助诊断具有重要意义。
  • 飞桨工具组件 - PaddleX
  • 飞桨全流程开发工具,集飞桨核心框架、模型库、工具及组件等深度学习开发所需全部能力于一身,打通深度学习开发全流程。
  • PaddleX同时提供简明易懂的Python API,及一键下载安装的图形化开发客户端。用户可根据实际生产需求选择相应的开发方式,获得飞桨全流程开发的最佳体验。

[0] 安装PaddleX

  • 此处默认为PaddleX 2.0.0版本。
!pip install paddlex --upgrade

[1] 导入依赖模块

import warnings
warnings.filterwarnings('ignore')

import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'

from paddlex import transforms as T
import paddlex as pdx
import paddle
from paddle.regularizer import L2Decay

import shutil
import glob

import numpy as np
import pandas as pd

import cv2
import imghdr
from PIL import Image

[2] 生成 VOC 格式文件夹

  • Paddle系列开发套件为VOC格式文件夹提供友好的支持。
  • 数据集结构如下,即可使用PaddleX自动进行数据划分:
Dataset/     # 语义分割数据集根目录
|--JPEGImages/ # 原图文件所在目录(默认不支持.TIF)
|  |--1.jpg
|  |--2.jpg
|  |--...
|  |--...
|
|--Annotations/ # 标注文件所在目录(注意为.PNG)
|  |--1.png
|  |--2.png
|  |--...
|  |--...
  • 尊重数据版权,“切记不要将本数据集上传到任何渠道的个人空间(包括飞桨公开数据库),若被数据提供方发现,将被严肃处理”。
  • 利用官网数据链接下载数据到 data/dataset.zip
!wget https://bj.bcebos.com/v1/dataset-bj/%E5%8C%BB%E7%96%97%E6%AF%94%E8%B5%9B/%E5%B8%B8%E8%A7%84%E8%B5%9B%EF%BC%9APALM%E7%9C%BC%E5%BA%95%E5%BD%A9%E7%85%A7%E8%A7%86%E7%9B%98%E6%8E%A2%E6%B5%8B%E4%B8%8E%E5%88%86%E5%89%B2.zip -O data/dataset.zip
  • 使用 pdx.utils.decompress 解压目标文件至同级目录下(解压后注意文件名乱码,可手动复制路径)。
  • 利用 mv 命令重命名至VOC格式文件夹。
pdx.utils.decompress('data/dataset.zip')

!rm -rf data/__MACOSX
!mv data/常规赛:PALM眼底彩照视盘探测与分割 data/dataset

!mv data/dataset/Train/fundus_image data/dataset/Train/JPEGImages
!mv data/dataset/Train/Disc_Masks data/dataset/Train/Annotations
!tree data/dataset/Train -L 1
data/dataset/Train
├── Annotations
└── JPEGImages

2 directories, 0 files

[3] 图片数据清洗

  • 图片的模式(mode)分别为 RGB(真彩色、三通道) 和 L(8位灰度图),统一转成JPG和PNG格式;

  • 图片的尺寸(size)统一为(1444,1444),因为有的 img/label 的尺寸不同。

  • 语义分割的标注图像,如1.png,为单通道图像,像素标注类别需要从0开始递增(一般0表示background背景), 例如0, 1, 2, 3表示4种类别,标注类别最多255个类别(其中像素值255不参与训练和评估)。

  • 此处将会把0-255变成0-1的PNG标注图。

jpg_path_list = glob.glob('data/dataset/Train/JPEGImages/*.jpg')

for i in range(len(jpg_path_list)):
    jpg_path = jpg_path_list[i]
    jpg_name = str(jpg_path.split('/')[-1]).split('.')[0]
    ant_path = os.path.join('data/dataset/Train/Annotations', f'{jpg_name}.bmp')

    if imghdr.what(jpg_path) and imghdr.what(ant_path):
        img_jpg = Image.open(jpg_path).convert('RGB')
        img_jpg = img_jpg.resize((1444, 1444))
        img_jpg.save(jpg_path)
        
        img_ant = Image.open(ant_path).convert('L')
        img_ant = img_ant.resize((1444, 1444))
        img_ant = np.array(img_ant, dtype='uint8')
        img_ant = np.uint8((img_ant == 0) * 1)
        cv2.imwrite(ant_path.split('.')[0]+'.png', img_ant)
        os.remove(ant_path)
    else:
        if os.path.exists(jpg_path):
            os.remove(jpg_path)
            print('delete:', jpg_path)
        if os.path.exists(ant_path):
            os.remove(ant_path)
            print('delete:', ant_path)

[4] 数据集划分

!paddlex --split_dataset --format SEG\
    --dataset_dir data/dataset/Train/\
    --val_value 0.25\
    --test_value 0

[5] 定义数据增强,并装载数据集

  • Mix-Up 数据增强操作在图像分类中更常用,但此处也能涨点。
  • 图像尺寸归一化为512x512,加上水平翻转和垂直翻转,最后归一化(PaddleX默认为ImageNet的mean和std)。
train_transforms = T.Compose([
    T.MixupImage(mixup_epoch=130),
    T.Resize(512),
    T.RandomHorizontalFlip(0.5),
    T.RandomVerticalFlip(0.5),
    T.Normalize()
])

eval_transforms = T.Compose([
    T.Resize(512),
    T.Normalize()
])
train_dataset = pdx.datasets.seg_dataset.SegDataset(
    data_dir='data/dataset/Train',
    file_list='data/dataset/Train/train_list.txt',
    label_list='data/dataset/Train/labels.txt',
    transforms=train_transforms,
    shuffle=True)

eval_dataset = pdx.datasets.seg_dataset.SegDataset(
    data_dir='data/dataset/Train',
    file_list='data/dataset/Train/val_list.txt',
    label_list='data/dataset/Train/labels.txt',
    transforms=eval_transforms)

[6] 配置 UNet 模型并训练

  • 图像分割任务中常出现类别不均衡的情况,而 Lovasz Loss 对这种情况有一定的改善。
model = pdx.seg.UNet(
    num_classes=len(train_dataset.labels),
    use_mixed_loss=[('LovaszSoftmaxLoss', 1)])
  • PaddleX内置了 Momentum 优化器和 polynomial 学习率衰减策略。
model.train(
    num_epochs=150,
    train_dataset=train_dataset,
    save_dir='output/UNet_Lovasz',
    eval_dataset=eval_dataset,
    train_batch_size=8,
    learning_rate=0.02,
    log_interval_steps=75,
    pretrain_weights='IMAGENET',
    save_interval_epochs=5,
    use_vdl=False)

[7] 模型预测,另存为提交图片

model = pdx.load_model('output/UNet_Lovasz/epoch_150')
model.get_model_info()
  • 单张图片的预测结果可视化。
import matplotlib.pyplot as plt
%matplotlib inline

img = cv2.imread('data/dataset/PALM-Testing400-Images/T0056.jpg')
result = model.predict(img.astype('float32'))

plt.figure(figsize=(8, 8))
plt.subplot(211)
plt.imshow(img[:, :, [2, 1, 0]])
plt.subplot(212)
plt.imshow(result['label_map'])
plt.show()
  • 生成提交文件夹 work/Disc_Segmentation,下载到本地压缩提交。
  • PIL.Image 方式读入图片并 convert('RGB') 防止脏数据,预测时将RGB转换成BGR通道。
  • 模型输出0-1转换成255-0,和赛题要求一致。
if not os.path.exists('work/Disc_Segmentation'):
    os.makedirs('work/Disc_Segmentation')

test_path_list = glob.glob('data/dataset/PALM-Testing400-Images/*.jpg')

for path in test_path_list:
    img = Image.open(path).convert('RGB')
    img = np.array(img, dtype='float32')

    result = model.predict(img[:, :, [2, 1, 0]])

    label_map = np.uint8((result['label_map'] == 0)*255)
    
    img_name = str(path.split('/')[-1]).split('.')[0]
    cv2.imwrite(f'work/Disc_Segmentation/{img_name}.png', label_map)
Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐