diff --git a/demo/demo_mace3D.py b/demo/demo_mace3D.py index f0cdc371bae99ccd373f03d1b34a4d24e6a64042..67a90337a6817ac3868958708ae01f50798eec53 100644 --- a/demo/demo_mace3D.py +++ b/demo/demo_mace3D.py @@ -44,9 +44,8 @@ save_path = './output/mace3D/' os.makedirs(save_path, exist_ok=True) # Geometry parameters -dist_source_detector = 839.0472 # Distance between the X-ray source and the detector in units of ALU +dist_source_detector = 4*839.0472 # Distance between the X-ray source and the detector in units of ALU magnification = 5.572128439964856 # magnification = (source to detector distance)/(source to center-of-rotation distance) -delta_pixel_detector = 0.25 # Scalar value of detector pixel spacing in units of ALU num_det_rows = 28 # number of detector rows num_det_channels = 240 # number of detector channels @@ -91,8 +90,7 @@ print("Generating sinogram ...") angles = np.linspace(0, 2 * np.pi, num_views, endpoint=False) sino = mbircone.cone3D.project(phantom, angles, num_det_rows, num_det_channels, - dist_source_detector, magnification, - delta_pixel_detector=delta_pixel_detector) + dist_source_detector, magnification) sino_weights = mbircone.cone3D.calc_weights(sino, weight_type='transmission') # Add transmission noise @@ -104,7 +102,6 @@ sino_noisy = sino + noise # ########################################################################### print("Performing qGGMRF reconstruction ...") recon_qGGMRF = mbircone.cone3D.recon(sino_noisy, angles, dist_source_detector, magnification, - delta_pixel_detector=delta_pixel_detector, sharpness=sharpness, weight_type='transmission', verbose=1) @@ -136,7 +133,6 @@ recon_mace = mbircone.mace.mace3D(sino_noisy, angles, dist_source_detector, magn max_admm_itr=max_admm_itr, init_image=recon_qGGMRF, sharpness=sharpness, - delta_pixel_detector=delta_pixel_detector, weight_type='transmission', verbose=1) recon_shape = recon_mace.shape diff --git a/demo/demo_mace3D_fast.py b/demo/demo_mace3D_fast.py index 7b7a8f86ae48a2530a3a88de0ac98ffddc474b95..81a6593d6cff1e9515a22dfd70dc0c1ceb9a43da 100644 --- a/demo/demo_mace3D_fast.py +++ b/demo/demo_mace3D_fast.py @@ -44,9 +44,8 @@ save_path = './output/mace3D_fast/' os.makedirs(save_path, exist_ok=True) # Geometry parameters -dist_source_detector = 839.0472 # Distance between the X-ray source and the detector in units of ALU +dist_source_detector = 4*839.0472 # Distance between the X-ray source and the detector in units of ALU magnification = 5.572128439964856 # magnification = (source to detector distance)/(source to center-of-rotation distance) -delta_pixel_detector = 0.25 # Scalar value of detector pixel spacing in units of ALU num_det_rows = 29 # number of detector rows num_det_channels = 120 # number of detector channels @@ -104,8 +103,7 @@ print("Generating sinogram ...") angles = np.linspace(0, 2 * np.pi, num_views, endpoint=False) sino = mbircone.cone3D.project(phantom, angles, num_det_rows, num_det_channels, - dist_source_detector, magnification, - delta_pixel_detector=delta_pixel_detector) + dist_source_detector, magnification) sino_weights = mbircone.cone3D.calc_weights(sino, weight_type='transmission') # Add transmission noise @@ -117,7 +115,6 @@ sino_noisy = sino + noise # ########################################################################### print("Performing qGGMRF reconstruction ...") recon_qGGMRF = mbircone.cone3D.recon(sino_noisy, angles, dist_source_detector, magnification, - delta_pixel_detector=delta_pixel_detector, sharpness=sharpness, weight_type='transmission', verbose=1) @@ -147,7 +144,6 @@ recon_mace = mbircone.mace.mace3D(sino_noisy, angles, dist_source_detector, magn max_admm_itr=max_admm_itr, init_image=recon_qGGMRF, sharpness=sharpness, - delta_pixel_detector=delta_pixel_detector, weight_type='transmission', verbose=1) recon_shape = recon_mace.shape diff --git a/demo/demo_mace3D_old.py b/demo/demo_mace3D_old.py new file mode 100644 index 0000000000000000000000000000000000000000..f0cdc371bae99ccd373f03d1b34a4d24e6a64042 --- /dev/null +++ b/demo/demo_mace3D_old.py @@ -0,0 +1,164 @@ +import os, sys +import numpy as np +import math +import urllib.request +import tarfile +import mbircone +import demo_utils, denoiser_utils + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' + +""" +This script is a demonstration of the mace3D reconstruction algorithm. Demo functionality includes + * downloading phantom and denoiser data from specified urls + * generating synthetic sinogram data by forward projecting the phantom and then adding transmission noise + * performing a 3D MACE and qGGMRF reconstructions and displaying them. +""" +print('This script is a demonstration of the mace3D reconstruction algorithm. Demo functionality includes \ +\n\t * downloading phantom and denoiser data from specified urls \ +\n\t * generating synthetic sinogram data by forward projecting the phantom and then adding transmission noise\ +\n\t * performing a 3D MACE and qGGMRF reconstructions and displaying them.') + +# ########################################################################### +# Set the parameters to get the data and do the recon +# ########################################################################### + +# Change the parameters below for your own use case. + +# The url to the data repo. +data_repo_url = 'https://github.com/cabouman/mbir_data/raw/master/' + +# Download url to the index file. +# This file will be used to retrieve urls to files that we are going to download +yaml_url = os.path.join(data_repo_url, 'index.yaml') + +# Choice of phantom file. +# These should be valid choices specified in the index file. +# The url to phantom data will be parsed from data_repo_url and the choices of phantom specified below. +phantom_name = 'bottle_cap_3D' + +# destination path to download and extract the phantom and NN weight files. +target_dir = './demo_data/' +# path to store output recon images +save_path = './output/mace3D/' +os.makedirs(save_path, exist_ok=True) + +# Geometry parameters +dist_source_detector = 839.0472 # Distance between the X-ray source and the detector in units of ALU +magnification = 5.572128439964856 # magnification = (source to detector distance)/(source to center-of-rotation distance) +delta_pixel_detector = 0.25 # Scalar value of detector pixel spacing in units of ALU +num_det_rows = 28 # number of detector rows +num_det_channels = 240 # number of detector channels + +# Simulated sinogram parameters +num_views = 75 # number of projection views +sino_noise_sigma = 0.01 # transmission noise level + +# MACE recon parameters +sharpness = 1.0 # Parameter to control regularization level of reconstruction. +max_admm_itr = 10 # max ADMM iterations for MACE reconstruction +# ######### End of parameters ######### + + +# ########################################################################### +# Download and extract data +# ########################################################################### + +# Download the url index file and return path to local file. +index_path = demo_utils.download_and_extract(yaml_url, target_dir) +# Load the url index file as a directionary +url_index = demo_utils.load_yaml(index_path) +# get urls to phantom and denoiser parameter file +phantom_url = os.path.join(data_repo_url, url_index['phantom'][phantom_name]) # url to download the 3D image volume phantom file +denoiser_url = os.path.join(data_repo_url, url_index['denoiser']['dncnn_ct']) # url to download the denoiser parameter file + +# download phantom file +phantom_path = demo_utils.download_and_extract(phantom_url, target_dir) +# download and extract NN weights and structure files used for MACE denoiser +denoiser_path = demo_utils.download_and_extract(denoiser_url, target_dir) + +# load original phantom +phantom = np.load(phantom_path) +print("shape of phantom = ", phantom.shape) + + +# ########################################################################### +# Generate sinogram +# ########################################################################### +print("Generating sinogram ...") + +# Generate view angles and sinogram with weights +angles = np.linspace(0, 2 * np.pi, num_views, endpoint=False) +sino = mbircone.cone3D.project(phantom, angles, + num_det_rows, num_det_channels, + dist_source_detector, magnification, + delta_pixel_detector=delta_pixel_detector) +sino_weights = mbircone.cone3D.calc_weights(sino, weight_type='transmission') + +# Add transmission noise +noise = sino_noise_sigma * 1. / np.sqrt(sino_weights) * np.random.normal(size=(num_views, num_det_rows, num_det_channels)) +sino_noisy = sino + noise + +# ########################################################################### +# Perform qGGMRF reconstruction +# ########################################################################### +print("Performing qGGMRF reconstruction ...") +recon_qGGMRF = mbircone.cone3D.recon(sino_noisy, angles, dist_source_detector, magnification, + delta_pixel_detector=delta_pixel_detector, + sharpness=sharpness, weight_type='transmission', + verbose=1) + +# ########################################################################### +# Set up the denoiser +# ########################################################################### +# The MACE reconstruction algorithm requires the use of a denoiser as a prior model. +# This demo includes a custom DnCNN denoiser trained on CT images, which can be used for other applications. +print("Loading denoiser function and model ...") +# Load denoiser model structure and pre-trained model weights +denoiser_model = denoiser_utils.DenoiserCT(checkpoint_dir=os.path.join(denoiser_path, 'model_dncnn_ct')) +# Define the denoiser using this model. This version requires some interface code to match with MACE. +def denoiser(img_noisy): + img_noisy = np.expand_dims(img_noisy, axis=1) + upper_range = denoiser_utils.calc_upper_range(img_noisy) + img_noisy = img_noisy/upper_range + testData_obj = denoiser_utils.DataLoader(img_noisy) + img_denoised = denoiser_model.denoise(testData_obj, batch_size=256) + img_denoised = img_denoised*upper_range + return np.squeeze(img_denoised) + + +# ########################################################################### +# Perform MACE reconstruction +# ########################################################################### +print("Performing MACE reconstruction ...") +recon_mace = mbircone.mace.mace3D(sino_noisy, angles, dist_source_detector, magnification, + denoiser=denoiser, denoiser_args=(), + max_admm_itr=max_admm_itr, + init_image=recon_qGGMRF, + sharpness=sharpness, + delta_pixel_detector=delta_pixel_detector, + weight_type='transmission', + verbose=1) +recon_shape = recon_mace.shape +print("Reconstruction shape = ", recon_shape) + + +# ########################################################################### +# Generating phantom and reconstruction images +# ########################################################################### +print("Generating phantom and reconstruction images ...") +# Plot axial slices of phantom and recon +display_slices = [7, 12, 17, 22] +for display_slice in display_slices: + demo_utils.plot_image(phantom[display_slice], title=f'phantom, axial slice {display_slice}', + filename=os.path.join(save_path, f'phantom_slice{display_slice}.png'), vmin=0, vmax=0.5) + demo_utils.plot_image(recon_mace[display_slice], title=f'MACE reconstruction, axial slice {display_slice}', + filename=os.path.join(save_path, f'recon_mace_slice{display_slice}.png'), vmin=0, vmax=0.5) + demo_utils.plot_image(recon_qGGMRF[display_slice], title=f'qGGMRF reconstruction, axial slice {display_slice}', + filename=os.path.join(save_path, f'recon_qGGMRF_slice{display_slice}.png'), vmin=0, vmax=0.5) +# Plot 3D phantom and recon image volumes as gif images. +demo_utils.plot_gif(phantom, save_path, 'phantom', vmin=0, vmax=0.5) +demo_utils.plot_gif(recon_mace, save_path, 'recon_mace', vmin=0, vmax=0.5) +demo_utils.plot_gif(recon_qGGMRF, save_path, 'recon_qGGMRF', vmin=0, vmax=0.5) + +input("press Enter")