def run_test(batch_size, img_name, seg_name, output_dir, device="cuda:0"): ds = NiftiDataset([img_name], [seg_name], transform=AddChannel(), seg_transform=AddChannel(), image_only=False) loader = DataLoader(ds, batch_size=1, pin_memory=torch.cuda.is_available()) net = UNet( dimensions=3, in_channels=1, out_channels=1, channels=(4, 8, 16, 32), strides=(2, 2, 2), num_res_units=2 ).to(device) roi_size = (16, 32, 48) sw_batch_size = batch_size def _sliding_window_processor(_engine, batch): net.eval() img, seg, meta_data = batch with torch.no_grad(): seg_probs = sliding_window_inference(img.to(device), roi_size, sw_batch_size, net, device=device) return predict_segmentation(seg_probs) infer_engine = Engine(_sliding_window_processor) SegmentationSaver( output_dir=output_dir, output_ext=".nii.gz", output_postfix="seg", batch_transform=lambda x: x[2] ).attach(infer_engine) infer_engine.run(loader) basename = os.path.basename(img_name)[: -len(".nii.gz")] saved_name = os.path.join(output_dir, basename, f"{basename}_seg.nii.gz") return saved_name
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 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 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 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 _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 test_inverse_array(self, use_compose, dtype, device): img: MetaTensor tr = Compose([ AddChannel(), Orientation("RAS"), Flip(1), Spacing([1.0, 1.2, 0.9], align_corners=False) ]) num_invertible = len( [i for i in tr.transforms if isinstance(i, InvertibleTransform)]) # forward img = tr(self.get_image(dtype, device)) self.assertEqual(len(img.applied_operations), num_invertible) # inverse with Compose if use_compose: img = tr.inverse(img) self.assertEqual(len(img.applied_operations), 0) # inverse individually else: _tr: InvertibleTransform num_to_inverse = num_invertible for _tr in tr.transforms[::-1]: if isinstance(_tr, InvertibleTransform): img = _tr.inverse(img) num_to_inverse -= 1 self.assertEqual(len(img.applied_operations), num_to_inverse)
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 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_epistemic_scoring(self): input_size = (20, 20, 20) device = "cuda" if torch.cuda.is_available() else "cpu" keys = ["image", "label"] num_training_ims = 10 train_data = self.get_data(num_training_ims, input_size) test_data = self.get_data(1, input_size) transforms = Compose([ AddChanneld(keys), CropForegroundd(keys, source_key="image"), DivisiblePadd(keys, 4), ]) infer_transforms = Compose([ AddChannel(), CropForeground(), DivisiblePad(4), ]) train_ds = CacheDataset(train_data, transforms) # output might be different size, so pad so that they match train_loader = DataLoader(train_ds, batch_size=2, collate_fn=pad_list_data_collate) model = UNet(3, 1, 1, channels=(6, 6), strides=(2, 2)).to(device) loss_function = DiceLoss(sigmoid=True) optimizer = torch.optim.Adam(model.parameters(), 1e-3) num_epochs = 10 for _ in trange(num_epochs): epoch_loss = 0 for batch_data in train_loader: inputs, labels = batch_data["image"].to( device), batch_data["label"].to(device) optimizer.zero_grad() outputs = model(inputs) loss = loss_function(outputs, labels) loss.backward() optimizer.step() epoch_loss += loss.item() epoch_loss /= len(train_loader) entropy_score = EpistemicScoring(model=model, transforms=infer_transforms, roi_size=[20, 20, 20], num_samples=10) # Call Individual Infer from Epistemic Scoring ip_stack = [test_data["image"], test_data["image"], test_data["image"]] ip_stack = np.array(ip_stack) score_3d = entropy_score.entropy_3d_volume(ip_stack) score_3d_sum = np.sum(score_3d) # Call Entropy Metric from Epistemic Scoring self.assertEqual(score_3d.shape, input_size) self.assertIsInstance(score_3d_sum, np.float32) self.assertGreater(score_3d_sum, 3.0)
def test_decollation_list(self, *transforms): t_compose = Compose([AddChannel(), Compose(transforms), ToTensor()]) # If nibabel present, read from disk if has_nib: t_compose = _ListCompose([LoadImage(image_only=False), t_compose]) dataset = Dataset(self.data_list, t_compose) self.check_decollate(dataset=dataset)
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 predict(model,image): image = AddChannel()(image) image = ToTensor()(image) output,_,_,_,_ = model(image) output = (output > 0.5) output = output.numpy() return output
def run_test(batch_size, img_name, seg_name, output_dir, device="cuda:0"): ds = ImageDataset([img_name], [seg_name], transform=AddChannel(), seg_transform=AddChannel(), image_only=True) loader = DataLoader(ds, batch_size=1, pin_memory=torch.cuda.is_available()) net = UNet(spatial_dims=3, in_channels=1, out_channels=1, channels=(4, 8, 16, 32), strides=(2, 2, 2), num_res_units=2).to(device) roi_size = (16, 32, 48) sw_batch_size = batch_size saver = SaveImage(output_dir=output_dir, output_ext=".nii.gz", output_postfix="seg") def _sliding_window_processor(_engine, batch): img = batch[0] # first item from ImageDataset is the input image with eval_mode(net): seg_probs = sliding_window_inference(img.to(device), roi_size, sw_batch_size, net, device=device) return predict_segmentation(seg_probs) def save_func(engine): if pytorch_after(1, 9, 1): for m in engine.state.output: saver(m) else: saver(engine.state.output[0]) infer_engine = Engine(_sliding_window_processor) infer_engine.add_event_handler(Events.ITERATION_COMPLETED, save_func) infer_engine.run(loader) basename = os.path.basename(img_name)[:-len(".nii.gz")] saved_name = os.path.join(output_dir, basename, f"{basename}_seg.nii.gz") return saved_name
def test_flatten_and_len(self): x = AddChannel() t1 = Compose([x, x, x, x, Compose([Compose([x, x]), x, x])]) t2 = t1.flatten() for t in t2.transforms: self.assertNotIsInstance(t, Compose) # test len self.assertEqual(len(t1), 8)
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 setUp(self): if not has_nib: self.skipTest("nibabel required for test_inverse") set_determinism(seed=0) self.all_data = {} affine = make_rand_affine() affine[0] *= 2 im_1d = AddChannel()(np.arange(0, 10)) self.all_data["1D"] = {"image": im_1d, "label": im_1d, "other": im_1d} im_2d_fname, seg_2d_fname = [make_nifti_image(i) for i in create_test_image_2d(101, 100)] im_3d_fname, seg_3d_fname = [make_nifti_image(i, affine) for i in create_test_image_3d(100, 101, 107)] load_ims = Compose([LoadImaged(KEYS), AddChanneld(KEYS)]) self.all_data["2D"] = load_ims({"image": im_2d_fname, "label": seg_2d_fname}) self.all_data["3D"] = load_ims({"image": im_3d_fname, "label": seg_3d_fname})
def predict_mask(model, images): image_array = [] for image in images: image_array.append(image.pixel_array) image_array = np.expand_dims( np.transpose(np.array(image_array).astype('float32')), (0, 1)) data_transforms = Compose([AddChannel(), NormalizeIntensity(), ToTensor()]) dataset = monai.data.Dataset(data=image_array, transform=data_transforms) print(dataset[0].shape) test_mask = sliding_window_inference(dataset[0], roi_size=[128, 128, 16], sw_batch_size=1, predictor=model) test_mask = test_mask.argmax(1).detach().cpu().numpy() test_mask = np.transpose(np.squeeze(test_mask, 0)) test_mask = test_mask.astype('uint8') test_mask = np.asarray(test_mask, order='C') return test_mask
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()
def test_err_msg(self): transforms = Compose([abs, AddChannel(), round], log_stats=False) with self.assertRaisesRegex(Exception, "AddChannel"): transforms(42.1)
import os import shutil import tempfile import unittest import nibabel as nib import numpy as np from parameterized import parameterized from monai.data import ArrayDataset from monai.transforms import AddChannel, Compose, LoadNifti, RandAdjustContrast, Spacing, RandGaussianNoise TEST_CASE_1 = [ Compose([ LoadNifti(image_only=True), AddChannel(), RandGaussianNoise(prob=1.0) ]), Compose([ LoadNifti(image_only=True), AddChannel(), RandGaussianNoise(prob=1.0) ]), (0, 1), (1, 128, 128, 128), ] TEST_CASE_2 = [ Compose([ LoadNifti(image_only=True), AddChannel(),
import os import shutil import tempfile import unittest import nibabel as nib import numpy as np from parameterized import parameterized from torch.utils.data import DataLoader from monai.data import ArrayDataset from monai.transforms import AddChannel, Compose, LoadNifti, RandAdjustContrast, RandGaussianNoise, Spacing TEST_CASE_1 = [ Compose([LoadNifti(image_only=True), AddChannel(), RandGaussianNoise(prob=1.0)]), Compose([LoadNifti(image_only=True), AddChannel(), RandGaussianNoise(prob=1.0)]), (0, 1), (1, 128, 128, 128), ] TEST_CASE_2 = [ Compose([LoadNifti(image_only=True), AddChannel(), RandAdjustContrast(prob=1.0)]), Compose([LoadNifti(image_only=True), AddChannel(), RandAdjustContrast(prob=1.0)]), (0, 1), (1, 128, 128, 128), ] class TestCompose(Compose): def __call__(self, input_):
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 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()]) ds = NiftiDataset(images, segs, transform=imtrans, seg_transform=segtrans, image_only=False) device = torch.device('cuda:0') 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, ) net.to(device) # define sliding window size and batch size for windows inference roi_size = (96, 96, 96) sw_batch_size = 4 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) return seg_probs, val_labels evaluator = Engine(_sliding_window_processor) # add evaluation metric to the evaluator engine MeanDice(add_sigmoid=True, to_onehot_y=False).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: predict_segmentation(output[0])) file_saver.attach(evaluator) # the model was trained by "unet_training_array" example ckpt_saver = CheckpointLoader(load_path='./runs/net_checkpoint_50.pth', 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) shutil.rmtree(tempdir)
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) ] )
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 = transforms.Compose([Rescale(), AddChannel()]) segtrans = transforms.Compose([AddChannel()]) ds = NiftiDataset(images, segs, transform=imtrans, seg_transform=segtrans, image_only=False) device = torch.device("cuda:0") 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,
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)