def process_slice(kspace, args): nkx, nky, nphases, ncoils = kspace.shape if 0 < args.crop_size < nkx: # crop along readout dimension images = fftc.ifftc(kspace, axis=0) images = center_crop(images, [args.crop_size, nky, nphases, ncoils]) kspace = fftc.fftc(images, axis=0).astype(np.complex64) nkx = args.crop_size # simulate reduced FOV #kspace = reduce_fov(kspace, ...) # compute time-average for ESPIRiT calibration kspace_avg = time_average(kspace, axis=-2) # ESPIRiT - compute sensitivity maps cmd = f'ecalib -d 0 -S -m {args.nmaps} -c {args.crop_value} -r {args.calib_size}' maps = bart.bart(1, cmd, kspace_avg[:, :, None, :]) maps = np.reshape(maps, (nkx, nky, 1, ncoils, args.nmaps)) # Convert everything to tensors kspace_tensor = cplx.to_tensor(kspace).unsqueeze(0) maps_tensor = cplx.to_tensor(maps).unsqueeze(0) # Do coil combination using sensitivity maps (PyTorch) A = T.SenseModel(maps_tensor) im_tensor = A(kspace_tensor, adjoint=True) # Convert tensor back to numpy array target = cplx.to_numpy(im_tensor.squeeze(0)) return kspace, maps, target
def preprocess(kspace, maps, args): # Batch size dimension must be the same! assert kspace.shape[0] == maps.shape[0] batch_size = kspace.shape[0] # Convert everything from numpy arrays to tensors kspace = cplx.to_tensor(kspace) maps = cplx.to_tensor(maps) # Initialize ESPIRiT model A = T.SenseModel(maps) # Compute normalization factor (based on 95% max signal level in view-shared dataa) averaged_kspace = T.time_average(kspace, dim=3) image = A(averaged_kspace, adjoint=True) magnitude_vals = cplx.abs(image).reshape(batch_size, -1) k = int(round(0.05 * magnitude_vals[0].numel())) scale = torch.min(torch.topk(magnitude_vals, k, dim=1).values, dim=1).values # Normalize k-space data kspace /= scale[:, None, None, None, None, None] # Compute network initialization if args.slwin_init: init_image = A(T.sliding_window(kspace, dim=3, window_size=5), adjoint=True) else: init_image = A(masked_kspace, adjoint=True) return kspace.unsqueeze(1), maps.unsqueeze(1), init_image.unsqueeze(1)
def process_slice(kspace, args, calib_method='jsense'): # get data dimensions nky, nkz, nechoes, ncoils = kspace.shape # ESPIRiT parameters nmaps = args.num_emaps calib_size = args.ncalib crop_value = args.crop_value if args.device is -1: device = sp.cpu_device else: device = sp.Device(args.device) # compute sensitivity maps (BART) #cmd = f'ecalib -d 0 -S -m {nmaps} -c {crop_value} -r {calib_size}' #maps = bart.bart(1, cmd, kspace[:,:,0,None,:]) #maps = np.reshape(maps, (nky, nkz, 1, ncoils, nmaps)) # compute sensitivity maps (SigPy) ksp = np.transpose(kspace[:, :, 0, :], [2, 1, 0]) if calib_method is 'espirit': maps = app.EspiritCalib(ksp, calib_width=calib_size, crop=crop_value, device=device, show_pbar=False).run() elif calib_method is 'jsense': maps = app.JsenseRecon(ksp, mps_ker_width=6, ksp_calib_width=calib_size, device=device, show_pbar=False).run() else: raise ValueError('%s calibration method not implemented...' % calib_method) maps = np.reshape(np.transpose(maps, [2, 1, 0]), (nky, nkz, 1, ncoils, nmaps)) # Convert everything to tensors kspace_tensor = cplx.to_tensor(kspace).unsqueeze(0) maps_tensor = cplx.to_tensor(maps).unsqueeze(0) # Do coil combination using sensitivity maps (PyTorch) A = T.SenseModel(maps_tensor) im_tensor = A(kspace_tensor, adjoint=True) # Convert tensor back to numpy array image = cplx.to_numpy(im_tensor.squeeze(0)) return image, maps
os.environ["TOOLBOX_PATH"] = toolbox_path sys.path.append(os.path.join(toolbox_path, 'python')) import bart, cfl # blocking params block_size = 16 block_stride = 16 # test dataset filename = '/data/sandino/Cine/validate/Exam2200_Series5_Phases20.h5' slice = 0 # pick slice with h5py.File(filename, 'r') as data: orig_images = data['target'][slice] # Convert numpy array to tensor images = cplx.to_tensor(orig_images).unsqueeze(0) _, nx, ny, nt, nmaps, _ = images.shape # Initialize blocking operator block_op = T.ArrayToBlocks(block_size, images.shape, overlapping=True) blocks = block_op(images) images = block_op(blocks, adjoint=True) images = images.squeeze(0) # Write out images images = cplx.to_numpy(images) cfl.writecfl('block_input', orig_images) cfl.writecfl('block_output', images) cfl.writecfl('block_error', orig_images - images)
def __call__(self, kspace, maps, target, attrs, fname, slice): """ Args: kspace (numpy.array): Input k-space of shape (num_coils, rows, cols, 2) for multi-coil data or (rows, cols, 2) for single coil data. target (numpy.array): Target image attrs (dict): Acquisition related information stored in the HDF5 object. fname (str): File name slice (int): Serial number of the slice. Returns: (tuple): tuple containing: image (torch.Tensor): Zero-filled input image. target (torch.Tensor): Target image converted to a torch Tensor. mean (float): Mean value used for normalization. std (float): Standard deviation value used for normalization. norm (float): L2 norm of the entire volume. """ seed = None if not self.use_seed else tuple(map(ord, fname)) # Convert everything from numpy arrays to tensors kspace = cplx.to_tensor(kspace).unsqueeze(0) maps = cplx.to_tensor(maps).unsqueeze(0) target = cplx.to_tensor(target).unsqueeze(0) norm = torch.sqrt(torch.mean(cplx.abs(target)**2)) # Apply random data augmentation kspace, target = self.augment(kspace, target, seed) # Undersample k-space data masked_kspace, mask = ss.subsample(kspace, self.mask_func, seed) # Initialize ESPIRiT model A = T.SenseModel(maps) # Compute normalization factor (based on 95% max signal level in view-shared dataa) averaged_kspace = T.time_average(masked_kspace, dim=3) image = A(averaged_kspace, adjoint=True) magnitude_vals = cplx.abs(image).reshape(-1) k = int(round(0.05 * magnitude_vals.numel())) scale = torch.min(torch.topk(magnitude_vals, k).values) # Normalize k-space and target images masked_kspace /= scale target /= scale mean = torch.tensor([0.0], dtype=torch.float32) std = scale # Compute network initialization if self.slwin_init: init_image = A(T.sliding_window(masked_kspace, dim=3, window_size=5), adjoint=True) else: init_image = A(masked_kspace, adjoint=True) # Get rid of batch dimension... masked_kspace = masked_kspace.squeeze(0) maps = maps.squeeze(0) init_image = init_image.squeeze(0) target = target.squeeze(0) return masked_kspace, maps, init_image, target, mean, std, norm
for i in range(len(batch_sizes)): batch_size = batch_sizes[i] print('Batch size: %d' % batch_size) for trial in range(num_trials): X = np.random.randn(batch_size, m, n) + 1j*np.random.randn(batch_size, m, n) X = X.astype(np.complex64) # perform batch-SVD (numpy) numpy_start_time = time.time() U, S, Vh = np.linalg.svd(X, full_matrices=False) numpy_end_time = time.time() numpy_times[i, trial] = numpy_end_time - numpy_start_time # convert matrix into a pytorch tensor X_torch = cplx.to_tensor(X) # perform SVD (pytorch, cpu) torch_start_time = time.time() U_torch, S_torch, V_torch = cplx.svd2(X_torch, compute_uv=True) torch_end_time = time.time() torch_times[i, trial] = torch_end_time - torch_start_time # perform batch-SVD (pytorch, gpu) X_torch = X_torch.cuda() gpu_start_time = time.time() U_gtorch, S_gtorch, V_gtorch = cplx.svd2(X_torch, compute_uv=True) gpu_end_time = time.time() torch_gpu_times[i, trial] = gpu_end_time - gpu_start_time # average over trials
def main(): # ARGS input_data_path = '/mnt/dense/data_public/fastMRI/multicoil_val' output_data_path = '/mnt/raid3/sandino/fastMRI/validate_full' center_fraction = 0.04 # number of k-space lines used to do ESPIRiT calib num_emaps = 1 dbwrite = False input_files = glob.glob(os.path.join(input_data_path, '*.h5')) for file in input_files: # Load HDF5 file hf = h5py.File(file, 'r') # existing keys: ['ismrmrd_header', 'kspace', 'reconstruction_rss'] # load k-space and image data from HDF5 file kspace_orig = hf['kspace'][()] im_rss = hf['reconstruction_rss'][()] # (33, 320, 320) # get data dimensions num_slices, num_coils, num_kx, num_ky = kspace_orig.shape xres, yres = im_rss.shape[1:3] # matrix size num_low_freqs = int(round(center_fraction * yres)) # allocate memory for new arrays im_shape = (xres, yres) kspace = np.zeros((num_slices, xres, yres, num_coils), dtype=np.complex64) maps = np.zeros((num_slices, xres, yres, num_coils, num_emaps), dtype=np.complex64) im_truth = np.zeros((num_slices, xres, yres, num_emaps), dtype=np.complex64) for sl in range(num_slices): kspace_slice = np.transpose(kspace_orig[sl], axes=[1, 2, 0]) kspace_slice = kspace_slice[:, :, None, :] # Data dimensions for BART: # kspace - (kx, ky, 1, coils) # maps - (kx, ky, 1, coils, emaps) # Data dimensions for PyTorch: # kspace - (1, kx, ky, coils, real/imag) # maps - (1, kx, ky, coils, emaps, real/imag) # Pre-process k-space data (PyTorch) kspace_tensor = cplx.to_tensor( np.transpose(kspace_slice, axes=[2, 0, 1, 3])) # (1, 640, 372, 15, 2) image_tensor = T.ifft2(kspace_tensor) print(image_tensor.size()) image_tensor = cplx.center_crop(image_tensor, im_shape) kspace_tensor = T.fft2(image_tensor) kspace_slice = np.transpose(cplx.to_numpy(kspace_tensor), axes=[1, 2, 0, 3]) # Compute sensitivity maps (BART) maps_slice = bart.bart( 1, f'ecalib -d 0 -m {num_emaps} -c 0.1 -r {num_low_freqs}', kspace_slice) maps_slice = np.reshape(maps_slice, (xres, yres, 1, num_coils, num_emaps)) maps_tensor = cplx.to_tensor( np.transpose(maps_slice, axes=[2, 0, 1, 3, 4])) # Do coil combination using sensitivity maps (PyTorch) A = T.SenseModel(maps_tensor) im_tensor = A(kspace_tensor, adjoint=True) # Convert image tensor to numpy array im_slice = cplx.to_numpy(im_tensor) # Re-shape and save everything kspace[sl] = np.reshape(kspace_slice, (xres, yres, num_coils)) maps[sl] = np.reshape(maps_slice, (xres, yres, num_coils, num_emaps)) im_truth[sl] = np.reshape(im_slice, (xres, yres, num_emaps)) # write out new hdf5 file_new = os.path.join(output_data_path, os.path.split(file)[-1]) with h5py.File(file_new, 'w') as hf_new: # create datasets within HDF5 hf_new.create_dataset('kspace', data=kspace) hf_new.create_dataset('maps', data=maps) hf_new.create_dataset('reconstruction_espirit', data=im_truth) hf_new.create_dataset('reconstruction_rss', data=im_rss) # provided by fastMRI hf_new.create_dataset('ismrmrd_header', data=hf['ismrmrd_header']) # create attributes (metadata) for key in hf.attrs.keys(): hf_new.attrs[key] = hf.attrs[key] if dbwrite: hf_new = h5py.File(file_new, 'r') print('Keys:', list(hf_new.keys())) print('Attrs:', dict(hf_new.attrs)) cfl.writecfl('/home/sandino/maps', hf_new['maps'][()]) cfl.writecfl('/home/sandino/kspace', hf_new['kspace'][()]) cfl.writecfl('/home/sandino/im_truth', hf_new['reconstruction_rss'][()]) cfl.writecfl('/home/sandino/im_recon', hf_new['reconstruction_espirit'][()])
def __call__(self, kspace, maps, target, attrs, fname, slice): """ Args: kspace (numpy.array): Input k-space of shape (num_coils, rows, cols, 2) for multi-coil data or (rows, cols, 2) for single coil data. target (numpy.array): Target image attrs (dict): Acquisition related information stored in the HDF5 object. fname (str): File name slice (int): Serial number of the slice. Returns: (tuple): tuple containing: image (torch.Tensor): Zero-filled input image. target (torch.Tensor): Target image converted to a torch Tensor. mean (float): Mean value used for normalization. std (float): Standard deviation value used for normalization. norm (float): L2 norm of the entire volume. """ # Convert everything from numpy arrays to tensors kspace = cplx.to_tensor(kspace).unsqueeze(0) maps = cplx.to_tensor(maps).unsqueeze(0) target = cplx.to_tensor(target).unsqueeze(0) norm = torch.sqrt(torch.mean(cplx.abs(target)**2)) #print(kspace.shape) #print(maps.shape) #print(target.shape) # Apply mask in k-space seed = None if not self.use_seed else tuple(map(ord, fname)) masked_kspace, mask = ss.subsample(kspace, self.mask_func, seed, mode='2D') # Normalize data... if 0: A = T.SenseModel(maps, weights=mask) image = A(masked_kspace, adjoint=True) magnitude = cplx.abs(image) elif 1: # ... by magnitude of zero-filled reconstruction A = T.SenseModel(maps) image = A(masked_kspace, adjoint=True) magnitude_vals = cplx.abs(image).reshape(-1) k = int(round(0.05 * magnitude_vals.numel())) scale = torch.min(torch.topk(magnitude_vals, k).values) else: # ... by power within calibration region calib_size = 10 calib_region = cplx.center_crop(masked_kspace, [calib_size, calib_size]) scale = torch.mean(cplx.abs(calib_region)**2) scale = scale * (calib_size**2 / kspace.size(-3) / kspace.size(-2)) masked_kspace /= scale target /= scale mean = torch.tensor([0.0], dtype=torch.float32) std = scale # Get rid of batch dimension... masked_kspace = masked_kspace.squeeze(0) maps = maps.squeeze(0) target = target.squeeze(0) return masked_kspace, maps, target, mean, std, norm