def predict(model: nn.Module, image: np.ndarray, image_size, normalize=A.Normalize(), batch_size=1) -> np.ndarray: tile_step = (image_size[0] // 2, image_size[1] // 2) tile_slicer = ImageSlicer(image.shape, image_size, tile_step) tile_merger = CudaTileMerger(tile_slicer.target_shape, 1, tile_slicer.weight) patches = tile_slicer.split(image) transform = A.Compose([normalize, A.Lambda(image=_tensor_from_rgb_image)]) data = list({ "image": patch, "coords": np.array(coords, dtype=np.int) } for (patch, coords) in zip(patches, tile_slicer.crops)) for batch in DataLoader(InMemoryDataset(data, transform), pin_memory=True, batch_size=batch_size): image = batch["image"].cuda(non_blocking=True) coords = batch["coords"] mask_batch = model(image) tile_merger.integrate_batch(mask_batch, coords) mask = tile_merger.merge() mask = np.moveaxis(to_numpy(mask), 0, -1) mask = tile_slicer.crop_to_orignal_size(mask) return mask
def test_tiles_split_merge_cuda(): if not torch.cuda.is_available(): return class MaxChannelIntensity(nn.Module): def __init__(self): super().__init__() def forward(self, input): max_channel, _ = torch.max(input, dim=1, keepdim=True) return max_channel image = np.random.random((5000, 5000, 3)).astype(np.uint8) tiler = ImageSlicer(image.shape, tile_size=(512, 512), tile_step=(256, 256), weight='pyramid') tiles = [tensor_from_rgb_image(tile) for tile in tiler.split(image)] model = MaxChannelIntensity().eval().cuda() merger = CudaTileMerger(tiler.target_shape, 1, tiler.weight) for tiles_batch, coords_batch in DataLoader(list(zip(tiles, tiler.crops)), batch_size=8, pin_memory=True): tiles_batch = tiles_batch.float().cuda() pred_batch = model(tiles_batch) merger.integrate_batch(pred_batch, coords_batch) merged = np.moveaxis(to_numpy(merger.merge()), 0, -1).astype(np.uint8) merged = tiler.crop_to_orignal_size(merged) np.testing.assert_equal(merged, image.max(axis=2, keepdims=True))
def infer_one(model, mask, tile_size=(512, 512), tile_step=(256, 256), weight='mean'): image = mask.cpu().numpy() image = np.moveaxis(image, 0, -1) with torch.no_grad(): tiler = ImageSlicer((900, 900), tile_size=tile_size, tile_step=tile_step, weight=weight) tiles = [np.moveaxis(tile, -1, 0) for tile in tiler.split(image)] merger = CudaTileMerger(tiler.target_shape, 1, tiler.weight) for tiles_batch, coords_batch in DataLoader(list( zip(tiles, tiler.crops)), batch_size=10, pin_memory=False): tiles_batch = tiles_batch.float().cuda() pred_batch = model(tiles_batch) tiles_batch.cpu().detach() merger.integrate_batch(pred_batch, coords_batch) merged_mask = np.moveaxis(to_numpy(merger.merge()), 0, -1) merged_mask = tiler.crop_to_orignal_size(merged_mask) m = merged_mask[..., 0].copy() return m
def inference_tiles(inference_model, img_full, device='cuda', shape=(32, 1, 768, 448), weight='mean', mean=88.904434, std=62.048634, plot=False): bs = shape[0] input_x = shape[2] input_y = shape[3] # Cut large image into overlapping tiles tiler = ImageSlicer(img_full.shape, tile_size=(input_x, input_y), tile_step=(input_x // 2, input_y // 2), weight=weight) # HCW -> CHW. Optionally, do normalization here tiles = [ tensor_from_rgb_image(tile) for tile in tiler.split(cv2.cvtColor(img_full, cv2.COLOR_GRAY2RGB)) ] # Allocate a CUDA buffer for holding entire mask merger = CudaTileMerger(tiler.target_shape, channels=1, weight=tiler.weight) # Run predictions for tiles and accumulate them for tiles_batch, coords_batch in DataLoader(list(zip(tiles, tiler.crops)), batch_size=bs, pin_memory=True): # Move tile to GPU tiles_batch = ((tiles_batch.float() - mean) / std).to(device) # Predict pred_batch = inference_model(tiles_batch) # Merge on GPU merger.integrate_batch(pred_batch, coords_batch) if plot: for i in range(pred_batch.to('cpu').numpy().shape[0]): plt.imshow(tiles_batch.to('cpu').numpy()[i, 0, :, :]) plt.show() plt.imshow(pred_batch.to('cpu').numpy()[i, 0, :, :]) plt.colorbar() plt.show() # Normalize accumulated mask and convert back to numpy merged_mask = np.moveaxis(to_numpy(merger.merge()), 0, -1).astype('float32') merged_mask = tiler.crop_to_orignal_size(merged_mask) torch.cuda.empty_cache() return merged_mask.squeeze()
def predict_mask(image, model, dims=3, size=394, step=192, batch_size=8, plot_image=False, dstdir=None, img_name='image1.png'): if image.ndim == 2: image = np.expand_dims(image, 2) if image.shape[-1] != dims: if image.shape[-1] == 1: image = np.repeat(image, 3, axis=2) elif image.shape[-1] == 3: image = np.expand_dims(image[:, :, 0], 2) print(image.shape) # Cut large image into overlapping tiles tiler = ImageSlicer(image.shape, tile_size=(size, size), tile_step=(step, step), weight='pyramid') # HCW -> CHW. Optionally, do normalization here tiles = [tensor_from_rgb_image(tile) for tile in tiler.split(image)] # Allocate a CUDA buffer for holding entire mask merger = CudaTileMerger(tiler.target_shape, 1, tiler.weight) # Run predictions for tiles and accumulate them with torch.no_grad(): for tiles_batch, coords_batch in DataLoader(list( zip(tiles, tiler.crops)), batch_size=batch_size, pin_memory=True): # print(tiles_batch.shape) tiles_batch = tiles_batch.float().cuda() pred_batch = model(tiles_batch) pred_mask = pred_batch.max(dim=1)[1].float() merger.integrate_batch(pred_mask, coords_batch) # Normalize accumulated mask and convert back to numpy merged_mask = np.moveaxis(to_numpy(merger.merge()), 0, -1).astype(np.uint8) merged_mask = tiler.crop_to_orignal_size(merged_mask) if plot_image: assert dstdir is not None, 'dstdir should be passed' fig, ax = plt.subplots(ncols=2, figsize=(20, 10)) ax[0].imshow(image[:, :, 0], cmap='gray') ax[1].imshow(merged_mask[:, :, 0], alpha=0.3) fig.savefig(osp.join(dstdir, img_name), bbox_inches='tight', pad_inches=0) print(osp.join(dstdir, img_name)) return merged_mask
def predict(model: nn.Module, image: np.ndarray, image_size, tta=None, normalize=A.Normalize(), batch_size=1, activation='sigmoid') -> np.ndarray: model.eval() tile_step = (image_size[0] // 2, image_size[1] // 2) tile_slicer = ImageSlicer(image.shape, image_size, tile_step, weight='pyramid') tile_merger = CudaTileMerger(tile_slicer.target_shape, 1, tile_slicer.weight) patches = tile_slicer.split(image) transform = A.Compose([normalize, A.Lambda(image=_tensor_from_rgb_image)]) if tta == 'fliplr': model = TTAWrapperFlipLR(model) print('Using FlipLR TTA') if tta == 'd4': model = TTAWrapperD4(model) print('Using D4 TTA') with torch.no_grad(): data = list({ 'image': patch, 'coords': np.array(coords, dtype=np.int) } for (patch, coords) in zip(patches, tile_slicer.crops)) for batch in DataLoader(InMemoryDataset(data, transform), pin_memory=True, batch_size=batch_size): image = batch['image'].cuda(non_blocking=True) coords = batch['coords'] mask_batch = model(image) tile_merger.integrate_batch(mask_batch, coords) mask = tile_merger.merge() if activation == 'sigmoid': mask = mask.sigmoid() if isinstance(activation, float): mask = F.relu(mask_batch - activation, inplace=True) mask = np.moveaxis(to_numpy(mask), 0, -1) mask = tile_slicer.crop_to_orignal_size(mask) return mask
def inference(inference_model, img_full, device='cuda'): x, y, ch = img_full.shape input_x = config['training']['crop_size'][0] input_y = config['training']['crop_size'][1] # Cut large image into overlapping tiles tiler = ImageSlicer(img_full.shape, tile_size=(input_x, input_y), tile_step=(input_x // 2, input_y // 2), weight=args.weight) # HCW -> CHW. Optionally, do normalization here tiles = [tensor_from_rgb_image(tile) for tile in tiler.split(img_full)] # Allocate a CUDA buffer for holding entire mask merger = CudaTileMerger(tiler.target_shape, channels=1, weight=tiler.weight) # Run predictions for tiles and accumulate them for tiles_batch, coords_batch in DataLoader(list(zip(tiles, tiler.crops)), batch_size=args.bs, pin_memory=True): # Move tile to GPU tiles_batch = (tiles_batch.float() / 255.).to(device) # Predict and move back to CPU pred_batch = inference_model(tiles_batch) # Merge on GPU merger.integrate_batch(pred_batch, coords_batch) # Plot if args.plot: for i in range(args.bs): if args.bs != 1: plt.imshow(pred_batch.cpu().detach().numpy().astype('float32').squeeze()[i, :, :]) else: plt.imshow(pred_batch.cpu().detach().numpy().astype('float32').squeeze()) plt.show() # Normalize accumulated mask and convert back to numpy merged_mask = np.moveaxis(to_numpy(merger.merge()), 0, -1).astype('float32') merged_mask = tiler.crop_to_orignal_size(merged_mask) # Plot if args.plot: for i in range(args.bs): if args.bs != 1: plt.imshow(merged_mask) else: plt.imshow(merged_mask.squeeze()) plt.show() torch.cuda.empty_cache() gc.collect() return merged_mask.squeeze()
def run_validation(data_df, model, data_folder, augmentation, tiles=False): total_dice_coeffs = [] mean_dice_per_image = [] for image_n in tqdm(range(data_df.shape[0])): image = cv2.imread( os.path.join(data_folder, data_df.index.values[image_n])) augmented = augmentation(image=image) image_processed = augmented['image'] if tiles: tiler = ImageSlicer(image_processed.shape[:2], tile_size=(224, 224), tile_step=(56, 56), weight='mean') merger = CudaTileMerger(tiler.target_shape, 4, tiler.weight) tiles = [ tensor_from_rgb_image(tile) for tile in tiler.split(image_processed) ] for tiles_batch, coords_batch in DataLoader(list( zip(tiles, tiler.crops)), batch_size=16, pin_memory=True): tiles_batch = tiles_batch.float().cuda() pred_batch = torch.nn.Sigmoid()(model(tiles_batch)) merger.integrate_batch(pred_batch, coords_batch) predictions = np.moveaxis(to_numpy(merger.merge()), 0, -1) predictions = tiler.crop_to_orignal_size(predictions) else: image_processed = torch.from_numpy( np.expand_dims(image_processed.transpose((2, 0, 1)), 0)).float() predictions = torch.nn.Sigmoid()(model( image_processed.cuda())[0]).detach().cpu().numpy() predictions = np.moveaxis(predictions, 0, -1) predictions_bin = (predictions > 0.5).astype(int) fname, masks = make_mask(image_n, data_df) dices_image = [] for defect_type in range(4): computed_dice = dice(masks[:, :, defect_type], predictions_bin[:, :, defect_type]) total_dice_coeffs.append(computed_dice) dices_image.append(computed_dice) mean_dice_per_image.append(np.mean(dices_image)) return np.mean(total_dice_coeffs), mean_dice_per_image
def predict_on_zslice_tiles(model, zimage, tile_size=(512, 512), tile_step=(256, 256)): image = zimage[0, 0, :, :] print(f'Stack shape:{zimage.shape}') print(f'Slice shape:{image.shape}') # Cut large image into overlapping tiles tiler = ImageSlicer(image.shape, tile_size=(512, 512), tile_step=(256, 256)) print(tiler.crops) # HCW -> CHW. Optionally, do normalization here tiles = [tensor_from_mask_image(tile) for tile in tiler.split(image)] # Allocate a CUDA buffer for holding entire mask merger = CudaTileMerger(tiler.target_shape, 1, tiler.weight) # Run predictions for tiles and accumulate them for tiles_batch, coords_batch in DataLoader(list(zip(tiles, tiler.crops)), batch_size=1, pin_memory=True): # for x, y, tile_width, tile_height in coords_batch: # tile = image[y : y + tile_height, x : x + tile_width].copy() tiles_batch = tiles_batch.float().cuda() pred_batch = model(tiles_batch) merger.integrate_batch(pred_batch, coords_batch) # Normalize accumulated mask and convert back to numpy # merged_mask = np.moveaxis(to_numpy(merger.merge()), 0, -1).astype(np.uint8) merged_mask = np.moveaxis(to_numpy(merger.merge()), 0, -1) merged_mask = tiler.crop_to_orignal_size(merged_mask) return merged_mask
def test_tiles_split_merge_non_dividable_cuda(): image = np.random.random((5632, 5120, 3)).astype(np.uint8) tiler = ImageSlicer(image.shape, tile_size=(1280, 1280), tile_step=(1280, 1280), weight='mean') tiles = tiler.split(image) merger = CudaTileMerger(tiler.target_shape, channels=image.shape[2], weight=tiler.weight) for tile, coordinates in zip(tiles, tiler.crops): # Integrate as batch of size 1 merger.integrate_batch( tensor_from_rgb_image(tile).unsqueeze(0).float().cuda(), [coordinates]) merged = merger.merge() merged = rgb_image_from_tensor(merged, mean=0, std=1, max_pixel_value=1) merged = tiler.crop_to_orignal_size(merged) np.testing.assert_equal(merged, image)
def predict_gradcam_mask(image, model, dims=3, size=(150, 300), step=(150, 300), batch_size=8, grad_thr=0.6, weight_type='mean', plot_image=False, dstdir=None, img_name='image1.png'): image = scale_img(image).astype(np.float32) if image.ndim == 2: image = np.expand_dims(image, 2) if image.shape[-1] != dims: if image.shape[-1] == 1: image = np.repeat(image, 3, axis=2) elif image.shape[-1] == 3: image = np.expand_dims(image[:, :, 0], 2) image = (image - np.min(image)) / (0.5 * np.ptp(image)) - 1 # Cut large image into overlapping tiles tiler = ImageSlicer(image.shape, tile_size=size, tile_step=step, weight=weight_type) # HCW -> CHW. Optionally, do normalization here tiles = [tensor_from_rgb_image(tile) for tile in tiler.split(image)] # Allocate a CUDA buffer for holding entire mask merger = CudaTileMerger(tiler.target_shape, 1, tiler.weight) # Run predictions for tiles and accumulate them for tiles_batch, coords_batch in DataLoader(list(zip(tiles, tiler.crops)), batch_size=batch_size, pin_memory=True): tiles_batch = tiles_batch.float().cuda() with torch.no_grad(): pred_batch = torch.max(F.softmax(model(tiles_batch), dim=1), dim=1)[1].detach().cpu().numpy() image_needed_classes = pred_batch == 1 masks = [] for tile_idx, has_target in enumerate(image_needed_classes): if has_target: tile = tiles_batch[tile_idx].unsqueeze(0) heatmap, mask = show_gradcam(tile, model) mask = mask[:, :, 0] mask[mask < grad_thr] = 0 masks.append(torch.Tensor(mask).unsqueeze(0).unsqueeze(0)) else: masks.append( torch.zeros_like(tiles_batch[tile_idx, 0]).unsqueeze(0).unsqueeze(0)) masks = torch.cat([mask.cuda() for mask in masks], dim=0) * 1000 merger.integrate_batch(masks, coords_batch) # Normalize accumulated mask and convert back to numpy merged_mask = np.moveaxis(to_numpy(merger.merge()), 0, -1).astype(np.uint8) merged_mask = tiler.crop_to_orignal_size(merged_mask) / 1000 if plot_image: assert dstdir is not None, 'dstdir should be passed' fig, ax = plt.subplots(ncols=2, figsize=(20, 10)) ax[0].imshow(image[:, :, 0], cmap='gray') ax[1].imshow(merged_mask[:, :, 0], alpha=0.3) fig.savefig(osp.join(dstdir, img_name), bbox_inches='tight', pad_inches=0) print(osp.join(dstdir, img_name)) return merged_mask
x, y, ch = img_full.shape mask_full = np.zeros((x, y)) # Cut large image into overlapping tiles tiler = ImageSlicer(img_full.shape, tile_size=(input_x, input_y), tile_step=(input_x // 2, input_y // 2), weight=args.weight) # HCW -> CHW. Optionally, do normalization here tiles = [tensor_from_rgb_image(tile) for tile in tiler.split(img_full)] # Allocate a CUDA buffer for holding entire mask merger = CudaTileMerger(tiler.target_shape, channels=1, weight=tiler.weight) # Loop evaluating inference on every fold masks = [] for fold in range(len(models)): # Run predictions for tiles and accumulate them for tiles_batch, coords_batch in DataLoader(list( zip(tiles, tiler.crops)), batch_size=args.bs, pin_memory=True): # Move tile to GPU tiles_batch = (tiles_batch.float() / 255.).to(device) # Predict and move back to CPU pred_batch = torch.sigmoid(