namijiang98's picture
Initial release: 12 checkpoints, inference and transfer code
9759eef verified
Raw
History Blame
4.56 kB
"""
Data-loading helpers for the HaN dose-prediction models.
Trimmed from the original research toolkit: only the functions the released
data loader and inference script actually call are kept, which drops the
cv2 / matplotlib / SimpleITK plotting dependencies.
Derived from the data pipeline of:
Riqiang Gao, Bin Lou, Zhoubing Xu, Dorin Comaniciu, Ali Kamen.
"Flexible-CM GAN: Towards Precise 3D Dose Prediction in Radiotherapy." CVPR 2023.
"""
import os
import numpy as np
import torch
import nibabel as nib
from monai.transforms import (
Compose,
Resized,
RandFlipd,
RandRotated,
SpatialPadd,
SpatialCropd,
RandSpatialCropd,
)
def combine_oar(tmp_dict, need_list,norm_oar = True, OAR_DICT = None):
'''
this function is used to support the data loader.
tmp_dict: the dictionary of the data loaded from the npz file
need_list: the list of the OARs needed to be combined
norm_oar: if True, the OARs will be normalized to the same scale
OAR_DICT: the dictionary of the OARs, the key is the name of the OAR, the value is the index of the OAR in the combined
'''
comb_oar = torch.zeros(tmp_dict['img'].shape)
cat_oar = torch.zeros([len(OAR_DICT)] + list(tmp_dict['img'].shape)[1:])
for key in OAR_DICT.keys():
if key not in need_list:
continue
if key in tmp_dict.keys():
single_oar = tmp_dict[key]
else:
single_oar = torch.zeros(tmp_dict['img'].shape)
cat_oar[OAR_DICT[key]-1: OAR_DICT[key]] = single_oar
if norm_oar:
comb_oar = torch.maximum(comb_oar, single_oar.round() * (1.0 + 4.0 * OAR_DICT[key] / 30))
else:
comb_oar = torch.maximum(comb_oar, single_oar.round() * OAR_DICT[key]) # changed in this version
return comb_oar, cat_oar
def combine_ptv(tmp_dict, scaled_dose_dict):
prescribed_dose = []
cat_ptv = torch.zeros([3] + list(tmp_dict['img'].shape)[1:])
prescribed_dose = [0] * 3
comb_ptv = torch.zeros(tmp_dict['img'].shape)
cnt = 0
for key in scaled_dose_dict.keys():
tmp_ptv = tmp_dict[key] * scaled_dose_dict[key]
prescribed_dose[cnt] = scaled_dose_dict[key]
cat_ptv[cnt] = tmp_ptv
comb_ptv = torch.maximum(comb_ptv, tmp_ptv)
cnt += 1
# sort the cat_ptv according to the prescribed dose
paired = [(cat_ptv[i], prescribed_dose[i]) for i in range(len(prescribed_dose))]
paired_sorted = sorted(paired, key=lambda x: x[1], reverse=True)
cat_ptv = torch.stack([x[0] for x in paired_sorted])
prescribed_dose = [x[1] for x in paired_sorted]
return comb_ptv, prescribed_dose, cat_ptv
def tr_augmentation(KEYS, in_size, out_size, crop_center):
return Compose([
SpatialCropd(keys = KEYS, roi_center = crop_center, roi_size = [int(in_size[0] * 1.2), int(in_size[1] * 1.2), int(in_size[2] * 1.2)], allow_missing_keys = True),
SpatialPadd(keys = KEYS, spatial_size = [int(in_size[0] * 1.2), int(in_size[1] * 1.2), int(in_size[2] * 1.2)], mode = 'constant', allow_missing_keys = True),
RandSpatialCropd(keys = KEYS, roi_size = [int(in_size[0] * 0.85), int(in_size[1] * 0.85), int(in_size[2] * 0.85)], max_roi_size = [int(in_size[0] * 1.2), int(in_size[1] * 1.2), int(in_size[2] * 1.2)], random_center = True, random_size = True, allow_missing_keys = True),
RandRotated(keys = KEYS, prob=0.8, range_x= 1, range_y = 0.2, range_z = 0.2, allow_missing_keys = True),
RandFlipd(keys = KEYS, prob = 0.4, spatial_axis = 0, allow_missing_keys = True),
RandFlipd(keys = KEYS, prob = 0.4, spatial_axis = 1, allow_missing_keys = True),
RandFlipd(keys = KEYS, prob = 0.4, spatial_axis = 2, allow_missing_keys = True),
Resized(keys = KEYS, spatial_size = out_size, allow_missing_keys = True),
])
def tt_augmentation(KEYS, in_size, out_size, crop_center):
return Compose([
SpatialCropd(keys = KEYS, roi_center = crop_center, roi_size = in_size, allow_missing_keys = True),
SpatialPadd(keys = KEYS, spatial_size = in_size, mode = 'constant', allow_missing_keys = True),
Resized(keys = KEYS, spatial_size = out_size, allow_missing_keys = True),
])
def load_nifti(path):
"""
Load a .nii or .nii.gz file and return a numpy array.
torch shape: [B, C, Z, Y, X] where B=1, C=1, Z=depth, Y=height, X=width
"""
nii = nib.load(path)
return np.transpose(nii.get_fdata(), (2, 1, 0)).astype(np.float32)