def main(tempdir): config.print_config() logging.basicConfig(stream=sys.stdout, level=logging.INFO) print(f"generating synthetic data to {tempdir} (this may take a while)") for i in range(5): im, seg = create_test_image_2d(128, 128, num_seg_classes=1) Image.fromarray((im * 255).astype("uint8")).save(os.path.join(tempdir, f"img{i:d}.png")) Image.fromarray((seg * 255).astype("uint8")).save(os.path.join(tempdir, f"seg{i:d}.png")) images = sorted(glob(os.path.join(tempdir, "img*.png"))) segs = sorted(glob(os.path.join(tempdir, "seg*.png"))) # define transforms for image and segmentation imtrans = Compose([LoadImage(image_only=True), AddChannel(), ScaleIntensity(), EnsureType()]) segtrans = Compose([LoadImage(image_only=True), AddChannel(), ScaleIntensity(), EnsureType()]) val_ds = ArrayDataset(images, imtrans, segs, segtrans) # sliding window inference for one image at every iteration val_loader = DataLoader(val_ds, batch_size=1, num_workers=1, pin_memory=torch.cuda.is_available()) dice_metric = DiceMetric(include_background=True, reduction="mean", get_not_nans=False) post_trans = Compose([EnsureType(), Activations(sigmoid=True), AsDiscrete(threshold=0.5)]) saver = SaveImage(output_dir="./output", output_ext=".png", output_postfix="seg") device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = UNet( spatial_dims=2, in_channels=1, out_channels=1, channels=(16, 32, 64, 128, 256), strides=(2, 2, 2, 2), num_res_units=2, ).to(device) model.load_state_dict(torch.load("best_metric_model_segmentation2d_array.pth")) model.eval() with torch.no_grad(): for val_data in val_loader: val_images, val_labels = val_data[0].to(device), val_data[1].to(device) # define sliding window size and batch size for windows inference roi_size = (96, 96) sw_batch_size = 4 val_outputs = sliding_window_inference(val_images, roi_size, sw_batch_size, model) val_outputs = [post_trans(i) for i in decollate_batch(val_outputs)] val_labels = decollate_batch(val_labels) # compute metric for current iteration dice_metric(y_pred=val_outputs, y=val_labels) for val_output in val_outputs: saver(val_output) # aggregate the final mean dice result print("evaluation metric:", dice_metric.aggregate().item()) # reset the status dice_metric.reset()
def run_inference_test(root_dir, test_x, test_y, device=torch.device("cuda:0")): # define transforms for image and classification val_transforms = Compose( [LoadPNG(image_only=True), AddChannel(), ScaleIntensity(), ToTensor()]) val_ds = MedNISTDataset(test_x, test_y, val_transforms) val_loader = DataLoader(val_ds, batch_size=300, num_workers=10) model = densenet121( spatial_dims=2, in_channels=1, out_channels=len(np.unique(test_y)), ).to(device) model_filename = os.path.join(root_dir, "best_metric_model.pth") model.load_state_dict(torch.load(model_filename)) model.eval() y_true = list() y_pred = list() with torch.no_grad(): for test_data in val_loader: test_images, test_labels = test_data[0].to( device), test_data[1].to(device) pred = model(test_images).argmax(dim=1) for i in range(len(pred)): y_true.append(test_labels[i].item()) y_pred.append(pred[i].item()) tps = [ np.sum((np.asarray(y_true) == idx) & (np.asarray(y_pred) == idx)) for idx in np.unique(test_y) ] return tps
def _define_prediction_transforms(self): """Define and initialize all prediction data transforms. * prediction set images transform * prediction set images post-transform @return True if data transforms could be instantiated, False otherwise. """ # Define transforms for prediction self._prediction_image_transforms = Compose( [ LoadImage(image_only=True), ScaleIntensity(), AddChannel(), ToTensor(), ] ) self._prediction_post_transforms = Compose( [ Activations(softmax=True), AsDiscrete(threshold_values=True), ] )
def main(): monai.config.print_config() logging.basicConfig(stream=sys.stdout, level=logging.INFO) # IXI dataset as a demo, downloadable from https://brain-development.org/ixi-dataset/ images = [ '/workspace/data/medical/ixi/IXI-T1/IXI607-Guys-1097-T1.nii.gz', '/workspace/data/medical/ixi/IXI-T1/IXI175-HH-1570-T1.nii.gz', '/workspace/data/medical/ixi/IXI-T1/IXI385-HH-2078-T1.nii.gz', '/workspace/data/medical/ixi/IXI-T1/IXI344-Guys-0905-T1.nii.gz', '/workspace/data/medical/ixi/IXI-T1/IXI409-Guys-0960-T1.nii.gz', '/workspace/data/medical/ixi/IXI-T1/IXI584-Guys-1129-T1.nii.gz', '/workspace/data/medical/ixi/IXI-T1/IXI253-HH-1694-T1.nii.gz', '/workspace/data/medical/ixi/IXI-T1/IXI092-HH-1436-T1.nii.gz', '/workspace/data/medical/ixi/IXI-T1/IXI574-IOP-1156-T1.nii.gz', '/workspace/data/medical/ixi/IXI-T1/IXI585-Guys-1130-T1.nii.gz' ] # 2 binary labels for gender classification: man and woman labels = np.array([ 0, 0, 1, 0, 1, 0, 1, 0, 1, 0 ]) # Define transforms for image val_transforms = Compose([ ScaleIntensity(), AddChannel(), Resize((96, 96, 96)), ToTensor() ]) # Define nifti dataset val_ds = NiftiDataset(image_files=images, labels=labels, transform=val_transforms, image_only=False) # create a validation data loader val_loader = DataLoader(val_ds, batch_size=2, num_workers=4, pin_memory=torch.cuda.is_available()) # Create DenseNet121 device = torch.device('cuda:0') model = monai.networks.nets.densenet.densenet121( spatial_dims=3, in_channels=1, out_channels=2, ).to(device) model.load_state_dict(torch.load('best_metric_model.pth')) model.eval() with torch.no_grad(): num_correct = 0. metric_count = 0 saver = CSVSaver(output_dir='./output') for val_data in val_loader: val_images, val_labels = val_data[0].to(device), val_data[1].to(device) val_outputs = model(val_images).argmax(dim=1) value = torch.eq(val_outputs, val_labels) metric_count += len(value) num_correct += value.sum().item() saver.save_batch(val_outputs, val_data[2]) metric = num_correct / metric_count print('evaluation metric:', metric) saver.finalize()
def test_range_scale(self): scaler = ScaleIntensity(minv=1.0, maxv=2.0) result = scaler(self.imt) mina = np.min(self.imt) maxa = np.max(self.imt) norm = (self.imt - mina) / (maxa - mina) expected = (norm * (2.0 - 1.0)) + 1.0 np.testing.assert_allclose(result, expected)
def initialize(self, args): """ `initialize` is called only once when the model is being loaded. Implementing `initialize` function is optional. This function allows the model to intialize any state associated with this model. """ # Pull model from google drive extract_dir = "/models/mednist_class/1" tar_save_path = os.path.join(extract_dir, model_filename) download_and_extract(gdrive_url, tar_save_path, output_dir=extract_dir, hash_val=md5_check, hash_type="md5") # load model configuration self.model_config = json.loads(args['model_config']) # create inferer engine and load PyTorch model inference_device_kind = args.get('model_instance_kind', None) logger.info(f"Inference device: {inference_device_kind}") self.inference_device = torch.device('cpu') if inference_device_kind is None or inference_device_kind == 'CPU': self.inference_device = torch.device('cpu') elif inference_device_kind == 'GPU': inference_device_id = args.get('model_instance_device_id', '0') logger.info(f"Inference device id: {inference_device_id}") if torch.cuda.is_available(): self.inference_device = torch.device( f'cuda:{inference_device_id}') cudnn.enabled = True else: logger.error( f"No CUDA device detected. Using device: {inference_device_kind}" ) # create pre-transforms for MedNIST self.pre_transforms = Compose([ LoadImage(reader="PILReader", image_only=True, dtype=np.float32), ScaleIntensity(), AddChannel(), AddChannel(), ToTensor(), Lambda(func=lambda x: x.to(device=self.inference_device)), ]) # create post-transforms self.post_transforms = Compose([ Lambda(func=lambda x: x.to(device="cpu")), ]) self.inferer = SimpleInferer() self.model = torch.jit.load( f'{pathlib.Path(os.path.realpath(__file__)).parent}{os.path.sep}model.pt', map_location=self.inference_device)
def run_test(batch_size=64, train_steps=200, device=torch.device("cuda:0")): class _TestBatch(Dataset): def __init__(self, transforms): self.transforms = transforms def __getitem__(self, _unused_id): im, seg = create_test_image_2d(128, 128, noise_max=1, num_objs=4, num_seg_classes=1) seed = np.random.randint(2147483647) self.transforms.set_random_state(seed=seed) im = self.transforms(im) self.transforms.set_random_state(seed=seed) seg = self.transforms(seg) return im, seg def __len__(self): return train_steps net = UNet( dimensions=2, in_channels=1, out_channels=1, channels=(4, 8, 16, 32), strides=(2, 2, 2), num_res_units=2, ).to(device) loss = DiceLoss(do_sigmoid=True) opt = torch.optim.Adam(net.parameters(), 1e-2) train_transforms = Compose([ AddChannel(), ScaleIntensity(), RandSpatialCrop((96, 96), random_size=False), RandRotate90(), ToTensor() ]) src = DataLoader(_TestBatch(train_transforms), batch_size=batch_size, shuffle=True) net.train() epoch_loss = 0 step = 0 for img, seg in src: step += 1 opt.zero_grad() output = net(img.to(device)) step_loss = loss(output, seg.to(device)) step_loss.backward() opt.step() epoch_loss += step_loss.item() epoch_loss /= step return epoch_loss, step
def default_normalizer(x) -> np.ndarray: """ A linear intensity scaling by mapping the (min, max) to (1, 0). """ if isinstance(x, torch.Tensor): x = x.detach().cpu().numpy() scaler = ScaleIntensity(minv=1.0, maxv=0.0) x = [scaler(x) for x in x] return np.stack(x, axis=0)
def __init__(self, dicom_folders): self.dicom_folders = dicom_folders self.transforms = get_validation_augmentation() self.preprocessing = get_preprocessing( functools.partial(preprocess_input, **formatted_settings)) self.transform3d = Compose( [ScaleIntensity(), Resize((160, 160, 160)), ToTensor()])
def main(): config.print_config() logging.basicConfig(stream=sys.stdout, level=logging.INFO) tempdir = tempfile.mkdtemp() print('generating synthetic data to {} (this may take a while)'.format(tempdir)) for i in range(5): im, seg = create_test_image_3d(128, 128, 128, num_seg_classes=1) n = nib.Nifti1Image(im, np.eye(4)) nib.save(n, os.path.join(tempdir, 'im%i.nii.gz' % i)) n = nib.Nifti1Image(seg, np.eye(4)) nib.save(n, os.path.join(tempdir, 'seg%i.nii.gz' % i)) images = sorted(glob(os.path.join(tempdir, 'im*.nii.gz'))) segs = sorted(glob(os.path.join(tempdir, 'seg*.nii.gz'))) # define transforms for image and segmentation imtrans = Compose([ScaleIntensity(), AddChannel(), ToTensor()]) segtrans = Compose([AddChannel(), ToTensor()]) val_ds = NiftiDataset(images, segs, transform=imtrans, seg_transform=segtrans, image_only=False) # sliding window inference for one image at every iteration val_loader = DataLoader(val_ds, batch_size=1, num_workers=1, pin_memory=torch.cuda.is_available()) device = torch.device('cuda:0') model = UNet( dimensions=3, in_channels=1, out_channels=1, channels=(16, 32, 64, 128, 256), strides=(2, 2, 2, 2), num_res_units=2, ).to(device) model.load_state_dict(torch.load('best_metric_model.pth')) model.eval() with torch.no_grad(): metric_sum = 0. metric_count = 0 saver = NiftiSaver(output_dir='./output') for val_data in val_loader: val_images, val_labels = val_data[0].to(device), val_data[1].to(device) # define sliding window size and batch size for windows inference roi_size = (96, 96, 96) sw_batch_size = 4 val_outputs = sliding_window_inference(val_images, roi_size, sw_batch_size, model) value = compute_meandice(y_pred=val_outputs, y=val_labels, include_background=True, to_onehot_y=False, add_sigmoid=True) metric_count += len(value) metric_sum += value.sum().item() val_outputs = (val_outputs.sigmoid() >= 0.5).float() saver.save_batch(val_outputs, val_data[2]) metric = metric_sum / metric_count print('evaluation metric:', metric) shutil.rmtree(tempdir)
def test_max_none(self): for p in TEST_NDARRAYS: scaler = ScaleIntensity(minv=0.0, maxv=None, factor=0.1) result = scaler(p(self.imt)) expected = rescale_array(p(self.imt), minv=0.0, maxv=None) assert_allclose(result, expected, type_test="tensor", rtol=1e-3, atol=1e-3)
def test_factor_scale(self): for p in TEST_NDARRAYS: scaler = ScaleIntensity(minv=None, maxv=None, factor=0.1) result = scaler(p(self.imt)) expected = p((self.imt * (1 + 0.1)).astype(np.float32)) assert_allclose(result, p(expected), type_test="tensor", rtol=1e-7, atol=0)
def main(tempdir): config.print_config() logging.basicConfig(stream=sys.stdout, level=logging.INFO) print(f"generating synthetic data to {tempdir} (this may take a while)") for i in range(5): im, seg = create_test_image_3d(128, 128, 128, num_seg_classes=1) n = nib.Nifti1Image(im, np.eye(4)) nib.save(n, os.path.join(tempdir, f"im{i:d}.nii.gz")) n = nib.Nifti1Image(seg, np.eye(4)) nib.save(n, os.path.join(tempdir, f"seg{i:d}.nii.gz")) images = sorted(glob(os.path.join(tempdir, "im*.nii.gz"))) segs = sorted(glob(os.path.join(tempdir, "seg*.nii.gz"))) # define transforms for image and segmentation imtrans = Compose([ScaleIntensity(), AddChannel(), ToTensor()]) segtrans = Compose([AddChannel(), ToTensor()]) val_ds = ImageDataset(images, segs, transform=imtrans, seg_transform=segtrans, image_only=False) # sliding window inference for one image at every iteration val_loader = DataLoader(val_ds, batch_size=1, num_workers=1, pin_memory=torch.cuda.is_available()) dice_metric = DiceMetric(include_background=True, reduction="mean") post_trans = Compose([Activations(sigmoid=True), AsDiscrete(threshold_values=True)]) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = UNet( dimensions=3, in_channels=1, out_channels=1, channels=(16, 32, 64, 128, 256), strides=(2, 2, 2, 2), num_res_units=2, ).to(device) model.load_state_dict(torch.load("best_metric_model_segmentation3d_array.pth")) model.eval() with torch.no_grad(): metric_sum = 0.0 metric_count = 0 saver = NiftiSaver(output_dir="./output") for val_data in val_loader: val_images, val_labels = val_data[0].to(device), val_data[1].to(device) # define sliding window size and batch size for windows inference roi_size = (96, 96, 96) sw_batch_size = 4 val_outputs = sliding_window_inference(val_images, roi_size, sw_batch_size, model) val_outputs = post_trans(val_outputs) value, _ = dice_metric(y_pred=val_outputs, y=val_labels) metric_count += len(value) metric_sum += value.item() * len(value) saver.save_batch(val_outputs, val_data[2]) metric = metric_sum / metric_count print("evaluation metric:", metric)
def default_normalizer(x) -> np.ndarray: """ A linear intensity scaling by mapping the (min, max) to (1, 0). N.B.: This will flip magnitudes (i.e., smallest will become biggest and vice versa). """ if isinstance(x, torch.Tensor): x = x.detach().cpu().numpy() scaler = ScaleIntensity(minv=1.0, maxv=0.0) x = [scaler(x) for x in x] return np.stack(x, axis=0)
def get_rsna_train_aug(name=None, image_size=160): return Compose([ ScaleIntensity(), Resize((image_size, image_size, image_size)), RandAffine(prob=0.5, translate_range=(5, 5, 5), rotate_range=(np.pi * 4, np.pi * 4, np.pi * 4), scale_range=(0.15, 0.15, 0.15), padding_mode='border'), ToTensor() ])
def test_range_scale(self): for p in TEST_NDARRAYS: scaler = ScaleIntensity(minv=1.0, maxv=2.0) result = scaler(p(self.imt)) mina = self.imt.min() maxa = self.imt.max() norm = (self.imt - mina) / (maxa - mina) expected = p((norm * (2.0 - 1.0)) + 1.0) assert_allclose(result, expected, type_test=False, rtol=1e-7, atol=0)
def test_range_scale(self, p): scaler = ScaleIntensity(minv=1.0, maxv=2.0) im = p(self.imt) result = scaler(im) mina = self.imt.min() maxa = self.imt.max() norm = (self.imt - mina) / (maxa - mina) expected = p((norm * (2.0 - 1.0)) + 1.0) assert_allclose(result, expected, type_test="tensor", rtol=1e-7, atol=0)
def test_channel_wise(self): for p in TEST_NDARRAYS: scaler = ScaleIntensity(minv=1.0, maxv=2.0, channel_wise=True) data = p(np.tile(self.imt, (3, 1, 1, 1))) result = scaler(data) mina = self.imt.min() maxa = self.imt.max() for i, c in enumerate(data): norm = (c - mina) / (maxa - mina) expected = p((norm * (2.0 - 1.0)) + 1.0) assert_allclose(result[i], expected, type_test=False, rtol=1e-7, atol=0)
def test_int(self): """integers should be handled by converting them to floats first.""" for p in TEST_NDARRAYS: scaler = ScaleIntensity(minv=1.0, maxv=2.0) result = scaler(p(self.imt.astype(int))) _imt = self.imt.astype(int).astype(np.float32) mina = _imt.min() maxa = _imt.max() norm = (_imt - mina) / (maxa - mina) expected = p((norm * (2.0 - 1.0)) + 1.0) assert_allclose(result, expected, type_test=False, rtol=1e-7, atol=0)
def transforms_image(image_bytes): image_transforms = Compose([ # LoadPNG(image_only=True), ScaleIntensity(), ToTensor() ]) print("t2") # print(type(image)) input = image_transforms(image_bytes) input_test = input.float() print("t3") input_predict = input_test.unsqueeze(0).unsqueeze(0) return input_predict
def classify_image(image): # make conversions to image and save temporarily im = Image.open(image) im = im.convert(mode='L') im = im.resize((64, 64)) im.save('conversion.jpeg', 'JPEG') # Define MONAI transforms, Dataset and Dataloader to process image val_transforms = Compose([ LoadImage(image_only=True), AddChannel(), ScaleIntensity(), ToTensor() ]) test_ds = MedNISTDataset(['conversion.jpeg'], [0], val_transforms) test_loader = torch.utils.data.DataLoader(test_ds) # Define Network device = torch.device("cpu") model = densenet121(spatial_dims=2, in_channels=1, out_channels=num_class).to(device) # Make prediction model.load_state_dict( torch.load( "SmartEMR_Imaging/MONAI_DATA_DIRECTORY/best_metric_model_cpu.pth")) model.eval() y_true = list() y_pred = list() with torch.no_grad(): for test_data in test_loader: test_images, test_labels = ( test_data[0].to(device), test_data[1].to(device), ) pred = model(test_images).argmax(dim=1) for i in range(len(pred)): y_true.append(test_labels[i].item()) y_pred.append(pred[i].item()) # clean up os.remove('conversion.jpeg') return class_tags[y_pred[0]]
def main(tempdir): config.print_config() logging.basicConfig(stream=sys.stdout, level=logging.INFO) print(f"generating synthetic data to {tempdir} (this may take a while)") for i in range(5): im, seg = create_test_image_3d(128, 128, 128, num_seg_classes=1) n = nib.Nifti1Image(im, np.eye(4)) nib.save(n, os.path.join(tempdir, f"im{i:d}.nii.gz")) n = nib.Nifti1Image(seg, np.eye(4)) nib.save(n, os.path.join(tempdir, f"seg{i:d}.nii.gz")) images = sorted(glob(os.path.join(tempdir, "im*.nii.gz"))) segs = sorted(glob(os.path.join(tempdir, "seg*.nii.gz"))) # define transforms for image and segmentation imtrans = Compose([ScaleIntensity(), AddChannel(), ToTensor()]) segtrans = Compose([AddChannel(), ToTensor()]) ds = ImageDataset(images, segs, transform=imtrans, seg_transform=segtrans, image_only=False) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") net = UNet( dimensions=3, in_channels=1, out_channels=1, channels=(16, 32, 64, 128, 256), strides=(2, 2, 2, 2), num_res_units=2, ).to(device) # define sliding window size and batch size for windows inference roi_size = (96, 96, 96) sw_batch_size = 4 post_trans = Compose( [Activations(sigmoid=True), AsDiscrete(threshold_values=True)]) def _sliding_window_processor(engine, batch): net.eval() with torch.no_grad(): val_images, val_labels = batch[0].to(device), batch[1].to(device) seg_probs = sliding_window_inference(val_images, roi_size, sw_batch_size, net) seg_probs = post_trans(seg_probs) return seg_probs, val_labels evaluator = Engine(_sliding_window_processor) # add evaluation metric to the evaluator engine MeanDice().attach(evaluator, "Mean_Dice") # StatsHandler prints loss at every iteration and print metrics at every epoch, # we don't need to print loss for evaluator, so just print metrics, user can also customize print functions val_stats_handler = StatsHandler( name="evaluator", output_transform=lambda x: None, # no need to print loss value, so disable per iteration output ) val_stats_handler.attach(evaluator) # for the array data format, assume the 3rd item of batch data is the meta_data file_saver = SegmentationSaver( output_dir="tempdir", output_ext=".nii.gz", output_postfix="seg", name="evaluator", batch_transform=lambda x: x[2], output_transform=lambda output: output[0], ) file_saver.attach(evaluator) # the model was trained by "unet_training_array" example ckpt_saver = CheckpointLoader( load_path="./runs_array/net_checkpoint_100.pt", load_dict={"net": net}) ckpt_saver.attach(evaluator) # sliding window inference for one image at every iteration loader = DataLoader(ds, batch_size=1, num_workers=1, pin_memory=torch.cuda.is_available()) state = evaluator.run(loader) print(state)
def main(): monai.config.print_config() logging.basicConfig(stream=sys.stdout, level=logging.INFO) # IXI dataset as a demo, downloadable from https://brain-development.org/ixi-dataset/ # the path of ixi IXI-T1 dataset data_path = os.sep.join( [".", "workspace", "data", "medical", "ixi", "IXI-T1"]) images = [ "IXI607-Guys-1097-T1.nii.gz", "IXI175-HH-1570-T1.nii.gz", "IXI385-HH-2078-T1.nii.gz", "IXI344-Guys-0905-T1.nii.gz", "IXI409-Guys-0960-T1.nii.gz", "IXI584-Guys-1129-T1.nii.gz", "IXI253-HH-1694-T1.nii.gz", "IXI092-HH-1436-T1.nii.gz", "IXI574-IOP-1156-T1.nii.gz", "IXI585-Guys-1130-T1.nii.gz", ] images = [os.sep.join([data_path, f]) for f in images] # 2 binary labels for gender classification: man and woman labels = np.array([0, 0, 1, 0, 1, 0, 1, 0, 1, 0], dtype=np.int64) # Define transforms for image val_transforms = Compose( [ScaleIntensity(), AddChannel(), Resize((96, 96, 96)), EnsureType()]) # Define image dataset val_ds = ImageDataset(image_files=images, labels=labels, transform=val_transforms, image_only=False) # create a validation data loader val_loader = DataLoader(val_ds, batch_size=2, num_workers=4, pin_memory=torch.cuda.is_available()) # Create DenseNet121 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = monai.networks.nets.DenseNet121(spatial_dims=3, in_channels=1, out_channels=2).to(device) model.load_state_dict( torch.load("best_metric_model_classification3d_array.pth")) model.eval() with torch.no_grad(): num_correct = 0.0 metric_count = 0 saver = CSVSaver(output_dir="./output") for val_data in val_loader: val_images, val_labels = val_data[0].to(device), val_data[1].to( device) val_outputs = model(val_images).argmax(dim=1) value = torch.eq(val_outputs, val_labels) metric_count += len(value) num_correct += value.sum().item() saver.save_batch(val_outputs, val_data[2]) metric = num_correct / metric_count print("evaluation metric:", metric) saver.finalize()
def main(tempdir): monai.config.print_config() logging.basicConfig(stream=sys.stdout, level=logging.INFO) # create a temporary directory and 40 random image, mask pairs print(f"generating synthetic data to {tempdir} (this may take a while)") for i in range(40): im, seg = create_test_image_2d(128, 128, num_seg_classes=1) Image.fromarray(im.astype("uint8")).save( os.path.join(tempdir, f"img{i:d}.png")) Image.fromarray(seg.astype("uint8")).save( os.path.join(tempdir, f"seg{i:d}.png")) images = sorted(glob(os.path.join(tempdir, "img*.png"))) segs = sorted(glob(os.path.join(tempdir, "seg*.png"))) train_files = [{ "img": img, "seg": seg } for img, seg in zip(images[:20], segs[:20])] val_files = [{ "img": img, "seg": seg } for img, seg in zip(images[-20:], segs[-20:])] # define transforms for image and segmentation train_imtrans = Compose([ LoadImage(image_only=True), ScaleIntensity(), AddChannel(), RandSpatialCrop((96, 96), random_size=False), RandRotate90(prob=0.5, spatial_axes=(0, 1)), ToTensor(), ]) train_segtrans = Compose([ LoadImage(image_only=True), AddChannel(), RandSpatialCrop((96, 96), random_size=False), RandRotate90(prob=0.5, spatial_axes=(0, 1)), ToTensor(), ]) val_imtrans = Compose([ LoadImage(image_only=True), ScaleIntensity(), AddChannel(), ToTensor() ]) val_segtrans = Compose( [LoadImage(image_only=True), AddChannel(), ToTensor()]) # define array dataset, data loader check_ds = ArrayDataset(images, train_imtrans, segs, train_segtrans) check_loader = DataLoader(check_ds, batch_size=10, num_workers=2, pin_memory=torch.cuda.is_available()) im, seg = monai.utils.misc.first(check_loader) print(im.shape, seg.shape) # create a training data loader train_ds = ArrayDataset(images[:20], train_imtrans, segs[:20], train_segtrans) train_loader = DataLoader(train_ds, batch_size=4, shuffle=True, num_workers=8, pin_memory=torch.cuda.is_available()) # create a validation data loader val_ds = ArrayDataset(images[-20:], val_imtrans, segs[-20:], val_segtrans) val_loader = DataLoader(val_ds, batch_size=1, num_workers=4, pin_memory=torch.cuda.is_available()) dice_metric = DiceMetric(include_background=True, reduction="mean") post_trans = Compose( [Activations(sigmoid=True), AsDiscrete(threshold_values=True)]) # create UNet, DiceLoss and Adam optimizer device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = monai.networks.nets.UNet( dimensions=2, in_channels=1, out_channels=1, channels=(16, 32, 64, 128, 256), strides=(2, 2, 2, 2), num_res_units=2, ).to(device) loss_function = monai.losses.DiceLoss(sigmoid=True) optimizer = torch.optim.Adam(model.parameters(), 1e-3) # start a typical PyTorch training val_interval = 2 best_metric = -1 best_metric_epoch = -1 epoch_loss_values = list() metric_values = list() writer = SummaryWriter() for epoch in range(10): print("-" * 10) print(f"epoch {epoch + 1}/{10}") model.train() epoch_loss = 0 step = 0 for batch_data in train_loader: step += 1 inputs, labels = batch_data[0].to(device), batch_data[1].to(device) optimizer.zero_grad() outputs = model(inputs) loss = loss_function(outputs, labels) loss.backward() optimizer.step() epoch_loss += loss.item() epoch_len = len(train_ds) // train_loader.batch_size print(f"{step}/{epoch_len}, train_loss: {loss.item():.4f}") writer.add_scalar("train_loss", loss.item(), epoch_len * epoch + step) epoch_loss /= step epoch_loss_values.append(epoch_loss) print(f"epoch {epoch + 1} average loss: {epoch_loss:.4f}") if (epoch + 1) % val_interval == 0: model.eval() with torch.no_grad(): metric_sum = 0.0 metric_count = 0 val_images = None val_labels = None val_outputs = None for val_data in val_loader: val_images, val_labels = val_data[0].to( device), val_data[1].to(device) roi_size = (96, 96) sw_batch_size = 4 val_outputs = sliding_window_inference( val_images, roi_size, sw_batch_size, model) val_outputs = post_trans(val_outputs) value, _ = dice_metric(y_pred=val_outputs, y=val_labels) metric_count += len(value) metric_sum += value.item() * len(value) metric = metric_sum / metric_count metric_values.append(metric) if metric > best_metric: best_metric = metric best_metric_epoch = epoch + 1 torch.save(model.state_dict(), "best_metric_model_segmentation2d_array.pth") print("saved new best metric model") print( "current epoch: {} current mean dice: {:.4f} best mean dice: {:.4f} at epoch {}" .format(epoch + 1, metric, best_metric, best_metric_epoch)) writer.add_scalar("val_mean_dice", metric, epoch + 1) # plot the last model output as GIF image in TensorBoard with the corresponding image and label plot_2d_or_3d_image(val_images, epoch + 1, writer, index=0, tag="image") plot_2d_or_3d_image(val_labels, epoch + 1, writer, index=0, tag="label") plot_2d_or_3d_image(val_outputs, epoch + 1, writer, index=0, tag="output") print( f"train completed, best_metric: {best_metric:.4f} at epoch: {best_metric_epoch}" ) writer.close()
return img, torch.tensor(row[target_cols]).float() # In[ ]: def default_collate(batch): data = torch.stack([item[0] for item in batch]) target = torch.stack([item[1] for item in batch]) # image labels. return data, target # In[ ]: train_transforms = Compose([ScaleIntensity(), Resize((image_size, image_size, image_size)), RandAffine( prob=0.5, # rotate_range=(np.pi * 2, np.pi * 2, np.pi * 2), scale_range=(0.15, 0.15, 0.15), padding_mode='border'), ToTensor()]) val_transforms = Compose([ScaleIntensity(),Resize((image_size, image_size, image_size)),ToTensor()]) # In[ ]: dataset_show = RSNADataset3D(df_study.head(5), 'train', transform=val_transforms) dataset_show_aug = RSNADataset3D(df_study.head(5), 'train', transform=train_transforms)
def _compute(data: np.ndarray) -> np.ndarray: scaler = ScaleIntensity(minv=1.0, maxv=0.0) return np.stack([scaler(i) for i in data], axis=0)
def main(): monai.config.print_config() logging.basicConfig(stream=sys.stdout, level=logging.INFO) # create a temporary directory and 40 random image, mask paris tempdir = tempfile.mkdtemp() print('generating synthetic data to {} (this may take a while)'.format(tempdir)) for i in range(40): im, seg = create_test_image_3d(128, 128, 128, num_seg_classes=1) n = nib.Nifti1Image(im, np.eye(4)) nib.save(n, os.path.join(tempdir, 'im%i.nii.gz' % i)) n = nib.Nifti1Image(seg, np.eye(4)) nib.save(n, os.path.join(tempdir, 'seg%i.nii.gz' % i)) images = sorted(glob(os.path.join(tempdir, 'im*.nii.gz'))) segs = sorted(glob(os.path.join(tempdir, 'seg*.nii.gz'))) # define transforms for image and segmentation train_imtrans = Compose([ ScaleIntensity(), AddChannel(), RandSpatialCrop((96, 96, 96), random_size=False), ToTensor() ]) train_segtrans = Compose([ AddChannel(), RandSpatialCrop((96, 96, 96), random_size=False), ToTensor() ]) val_imtrans = Compose([ ScaleIntensity(), AddChannel(), Resize((96, 96, 96)), ToTensor() ]) val_segtrans = Compose([ AddChannel(), Resize((96, 96, 96)), ToTensor() ]) # define nifti dataset, data loader check_ds = NiftiDataset(images, segs, transform=train_imtrans, seg_transform=train_segtrans) check_loader = DataLoader(check_ds, batch_size=10, num_workers=2, pin_memory=torch.cuda.is_available()) im, seg = monai.utils.misc.first(check_loader) print(im.shape, seg.shape) # create a training data loader train_ds = NiftiDataset(images[:20], segs[:20], transform=train_imtrans, seg_transform=train_segtrans) train_loader = DataLoader(train_ds, batch_size=5, shuffle=True, num_workers=8, pin_memory=torch.cuda.is_available()) # create a validation data loader val_ds = NiftiDataset(images[-20:], segs[-20:], transform=val_imtrans, seg_transform=val_segtrans) val_loader = DataLoader(val_ds, batch_size=5, num_workers=8, pin_memory=torch.cuda.is_available()) # create UNet, DiceLoss and Adam optimizer net = monai.networks.nets.UNet( dimensions=3, in_channels=1, out_channels=1, channels=(16, 32, 64, 128, 256), strides=(2, 2, 2, 2), num_res_units=2, ) loss = monai.losses.DiceLoss(do_sigmoid=True) lr = 1e-3 opt = torch.optim.Adam(net.parameters(), lr) device = torch.device('cuda:0') # ignite trainer expects batch=(img, seg) and returns output=loss at every iteration, # user can add output_transform to return other values, like: y_pred, y, etc. trainer = create_supervised_trainer(net, opt, loss, device, False) # adding checkpoint handler to save models (network params and optimizer stats) during training checkpoint_handler = ModelCheckpoint('./runs/', 'net', n_saved=10, require_empty=False) trainer.add_event_handler(event_name=Events.EPOCH_COMPLETED, handler=checkpoint_handler, to_save={'net': net, 'opt': opt}) # StatsHandler prints loss at every iteration and print metrics at every epoch, # we don't set metrics for trainer here, so just print loss, user can also customize print functions # and can use output_transform to convert engine.state.output if it's not a loss value train_stats_handler = StatsHandler(name='trainer') train_stats_handler.attach(trainer) # TensorBoardStatsHandler plots loss at every iteration and plots metrics at every epoch, same as StatsHandler train_tensorboard_stats_handler = TensorBoardStatsHandler() train_tensorboard_stats_handler.attach(trainer) validation_every_n_epochs = 1 # Set parameters for validation metric_name = 'Mean_Dice' # add evaluation metric to the evaluator engine val_metrics = {metric_name: MeanDice(add_sigmoid=True, to_onehot_y=False)} # ignite evaluator expects batch=(img, seg) and returns output=(y_pred, y) at every iteration, # user can add output_transform to return other values evaluator = create_supervised_evaluator(net, val_metrics, device, True) @trainer.on(Events.EPOCH_COMPLETED(every=validation_every_n_epochs)) def run_validation(engine): evaluator.run(val_loader) # add early stopping handler to evaluator early_stopper = EarlyStopping(patience=4, score_function=stopping_fn_from_metric(metric_name), trainer=trainer) evaluator.add_event_handler(event_name=Events.EPOCH_COMPLETED, handler=early_stopper) # add stats event handler to print validation stats via evaluator val_stats_handler = StatsHandler( name='evaluator', output_transform=lambda x: None, # no need to print loss value, so disable per iteration output global_epoch_transform=lambda x: trainer.state.epoch) # fetch global epoch number from trainer val_stats_handler.attach(evaluator) # add handler to record metrics to TensorBoard at every validation epoch val_tensorboard_stats_handler = TensorBoardStatsHandler( output_transform=lambda x: None, # no need to plot loss value, so disable per iteration output global_epoch_transform=lambda x: trainer.state.epoch) # fetch global epoch number from trainer val_tensorboard_stats_handler.attach(evaluator) # add handler to draw the first image and the corresponding label and model output in the last batch # here we draw the 3D output as GIF format along Depth axis, at every validation epoch val_tensorboard_image_handler = TensorBoardImageHandler( batch_transform=lambda batch: (batch[0], batch[1]), output_transform=lambda output: predict_segmentation(output[0]), global_iter_transform=lambda x: trainer.state.epoch ) evaluator.add_event_handler(event_name=Events.EPOCH_COMPLETED, handler=val_tensorboard_image_handler) train_epochs = 30 state = trainer.run(train_loader, train_epochs) shutil.rmtree(tempdir)
# monai_model_file = "output_jan/5foldmonai/monai3d_160_3ch_1e-5_20ep_aug_6targets_1_0.2838641107082367(1).pth" # print("use monai model:", monai_model_file) target_cols = [ 'rv_lv_ratio_gte_1', # exam level "central_pe", "leftsided_pe", "rightsided_pe", "acute_and_chronic_pe", "chronic_pe" ] out_dim = len(target_cols) image_size = 100 val_transforms = Compose([ ScaleIntensity(), Resize((image_size, image_size, image_size)), ToTensor() ]) val_transforms.set_random_state(seed=42) def monai_preprocess(imgs512): imgs = imgs512[:, :, 43:-55, 43:-55] img_monai = imgs[int(imgs.shape[0] * 0.25):int(imgs.shape[0] * 0.75)] img_monai = np.transpose(img_monai, (1, 2, 3, 0)) img_monai = apply_transform(val_transforms, img_monai) img_monai = np.expand_dims(img_monai, axis=0) img_monai = torch.from_numpy(img_monai).cuda() return img_monai
def run_training_test(root_dir, train_x, train_y, val_x, val_y, device="cuda:0", num_workers=10): monai.config.print_config() # define transforms for image and classification train_transforms = Compose([ LoadPNG(image_only=True), AddChannel(), ScaleIntensity(), RandRotate(range_x=np.pi / 12, prob=0.5, keep_size=True), RandFlip(spatial_axis=0, prob=0.5), RandZoom(min_zoom=0.9, max_zoom=1.1, prob=0.5), ToTensor(), ]) train_transforms.set_random_state(1234) val_transforms = Compose( [LoadPNG(image_only=True), AddChannel(), ScaleIntensity(), ToTensor()]) # create train, val data loaders train_ds = MedNISTDataset(train_x, train_y, train_transforms) train_loader = DataLoader(train_ds, batch_size=300, shuffle=True, num_workers=num_workers) val_ds = MedNISTDataset(val_x, val_y, val_transforms) val_loader = DataLoader(val_ds, batch_size=300, num_workers=num_workers) model = densenet121(spatial_dims=2, in_channels=1, out_channels=len(np.unique(train_y))).to(device) loss_function = torch.nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), 1e-5) epoch_num = 4 val_interval = 1 # start training validation best_metric = -1 best_metric_epoch = -1 epoch_loss_values = list() metric_values = list() model_filename = os.path.join(root_dir, "best_metric_model.pth") for epoch in range(epoch_num): print("-" * 10) print(f"Epoch {epoch + 1}/{epoch_num}") model.train() epoch_loss = 0 step = 0 for batch_data in train_loader: step += 1 inputs, labels = batch_data[0].to(device), batch_data[1].to(device) optimizer.zero_grad() outputs = model(inputs) loss = loss_function(outputs, labels) loss.backward() optimizer.step() epoch_loss += loss.item() epoch_loss /= step epoch_loss_values.append(epoch_loss) print(f"epoch {epoch + 1} average loss:{epoch_loss:0.4f}") if (epoch + 1) % val_interval == 0: model.eval() with torch.no_grad(): y_pred = torch.tensor([], dtype=torch.float32, device=device) y = torch.tensor([], dtype=torch.long, device=device) for val_data in val_loader: val_images, val_labels = val_data[0].to( device), val_data[1].to(device) y_pred = torch.cat([y_pred, model(val_images)], dim=0) y = torch.cat([y, val_labels], dim=0) auc_metric = compute_roc_auc(y_pred, y, to_onehot_y=True, softmax=True) metric_values.append(auc_metric) acc_value = torch.eq(y_pred.argmax(dim=1), y) acc_metric = acc_value.sum().item() / len(acc_value) if auc_metric > best_metric: best_metric = auc_metric best_metric_epoch = epoch + 1 torch.save(model.state_dict(), model_filename) print("saved new best metric model") print( f"current epoch {epoch +1} current AUC: {auc_metric:0.4f} " f"current accuracy: {acc_metric:0.4f} best AUC: {best_metric:0.4f} at epoch {best_metric_epoch}" ) print( f"train completed, best_metric: {best_metric:0.4f} at epoch: {best_metric_epoch}" ) return epoch_loss_values, best_metric, best_metric_epoch
def _define_training_transforms(self): """Define and initialize all training data transforms. * training set images transform * training set masks transform * validation set images transform * validation set masks transform * validation set images post-transform * test set images transform * test set masks transform * test set images post-transform * prediction set images transform * prediction set images post-transform @return True if data transforms could be instantiated, False otherwise. """ if self._mask_type == MaskType.UNKNOWN: raise Exception("The mask type is unknown. Cannot continue!") # Depending on the mask type, we will need to adapt the Mask Loader # and Transform. We start by initializing the most common types. MaskLoader = LoadMask(self._mask_type) MaskTransform = Identity # Adapt the transform for the LABEL types if self._mask_type == MaskType.TIFF_LABELS or self._mask_type == MaskType.NUMPY_LABELS: MaskTransform = ToOneHot(num_classes=self._out_channels) # The H5_ONE_HOT type requires a different loader if self._mask_type == MaskType.H5_ONE_HOT: # MaskLoader: still missing raise Exception("HDF5 one-hot masks are not supported yet!") # Define transforms for training self._train_image_transforms = Compose( [ LoadImage(image_only=True), ScaleIntensity(), AddChannel(), RandSpatialCrop(self._roi_size, random_size=False), RandRotate90(prob=0.5, spatial_axes=(0, 1)), ToTensor() ] ) self._train_mask_transforms = Compose( [ MaskLoader, MaskTransform, RandSpatialCrop(self._roi_size, random_size=False), RandRotate90(prob=0.5, spatial_axes=(0, 1)), ToTensor() ] ) # Define transforms for validation self._validation_image_transforms = Compose( [ LoadImage(image_only=True), ScaleIntensity(), AddChannel(), ToTensor() ] ) self._validation_mask_transforms = Compose( [ MaskLoader, MaskTransform, ToTensor() ] ) # Define transforms for testing self._test_image_transforms = Compose( [ LoadImage(image_only=True), ScaleIntensity(), AddChannel(), ToTensor() ] ) self._test_mask_transforms = Compose( [ MaskLoader, MaskTransform, ToTensor() ] ) # Post transforms self._validation_post_transforms = Compose( [ Activations(softmax=True), AsDiscrete(threshold_values=True) ] ) self._test_post_transforms = Compose( [ Activations(softmax=True), AsDiscrete(threshold_values=True) ] )